home *** CD-ROM | disk | FTP | other *** search
/ Scene Storm / Scene Storm - Volume 1.iso / coding / c / snoopdos_source / patches.c < prev    next >
C/C++ Source or Header  |  1996-02-16  |  139KB  |  4,804 lines

  1. /*
  2.  *        PATCHES.C                                    vi:ts=4
  3.  *
  4.  *      Copyright (c) Eddy Carroll, September 1994.
  5.  *
  6.  *        Controls the patching of dos.library and other functions in a
  7.  *        reliable manner.
  8.  */
  9.  
  10. #define DEBUG_PATTERN        0
  11.  
  12. #define MONITOR_SEMAPHORE    0
  13. #define STACK_CHECK            0        /* Not currently used */
  14.  
  15. #pragma libcall SysBase RawPutChar 204 001
  16.  
  17. #include "system.h"
  18. #include "snoopdos.h"
  19. #include "patches.h"
  20.  
  21. /*
  22.  *        These four pointers are imported from PATCHCODE.S, which is
  23.  *        also where the real code size is determined.
  24.  */
  25. #define CODESIZE        88    /* Must equal PatchCode_End - PatchCode_Start   */
  26. #define STACKADJUST        14    /* Must equal pc_JumpOrigFunc - pc_NormalReturn */
  27.  
  28. #define PatchCode_Size          (PatchCode_End - PatchCode_Start)
  29. #define PatchCode_StackAdjust (PatchCode_JumpOrigFunc - PatchCode_NormalReturn)
  30.  
  31. extern char far PatchCode_Start[];
  32. extern char far PatchCode_NormalReturn[];
  33. extern char far PatchCode_JumpOrigFunc[];
  34. extern char far PatchCode_End[];
  35.  
  36. #define ASM    __asm __saveds
  37.  
  38. #if MONITOR_SEMAPHORE
  39. Task *LoadTask;                /* Indicates we're inside LoadSeg() */
  40. #endif
  41.  
  42. /*
  43.  *        Now some packet IDs not defined by Commodore in dos/dosextens.h
  44.  */
  45. #define ACTION_DOUBLE          2000    /* Conman: create pipe filehandle    */
  46. #define ACTION_FORCE          2001    /* Conman: Force input into handler    */
  47. #define ACTION_DROP              2004    /* Conman: Discard all queued input    */
  48.  
  49. #define ACTION_GET_DISK_FSSM  4201    /* Get disk startup message            */
  50. #define ACTION_FREE_DISK_FSSM 4202    /* Free disk startup message        */
  51.  
  52. /*
  53.  *        Next, our three message types for the background process. These
  54.  *        correspond to pattern and pathname expansion messages.
  55.  */
  56. #define QUIT_MSG        0
  57. #define PATTERN_MSG        1
  58. #define PATHEXPAND_MSG    2
  59.  
  60. /*
  61.  *        Some miscellaneous strings
  62.  */
  63. char LinkPointerString[] = " --> ";
  64.  
  65. /*
  66.  *        I needed a quick way to hook into ReleaseSemaphore() to try and
  67.  *        figure out why ramlib would sometimes crash on calling it. What
  68.  *        better way to do this than by re-using one of our existing
  69.  *        patched functions (one which takes a parameter in A0). OpenDevice
  70.  *        fits the bill nicely (we just ignore the additional parameters).
  71.  */
  72. #if MONITOR_SEMAPHORE
  73. #undef LVO_OpenDevice
  74. #define LVO_OpenDevice        -570 // ReleaseSemaphore() LVO vector
  75. #endif
  76.  
  77. /*
  78.  *        This structure is used to communicate between the PutMsg() patch
  79.  *        and the background SnoopDos process when doing path expansion
  80.  *        for ACTION_MAKE_LINK or ShowFullPaths (when we're monitoring packets,
  81.  *        we can't guarantee that the calling processes' message port is free).
  82.  */
  83. typedef struct NameFromLockMsg {
  84.     struct Message  msg;        /* Standard exec message                */
  85.     int                type;        /* Type of this message                    */
  86.     BPTR            lock;        /* Lock to calculate path relative to    */
  87.     char            *filename;    /* Filename relative to lock            */
  88.     char            *buf;        /* Buffer to store result in            */
  89.     int                maxlen;        /* Maximum length of buffer                */
  90.     char            *newbuf;    /* On return, points to path in buffer     */
  91.     int                sigmask;    /* Signal number to signal when done    */
  92.     Task            *task;        /* Task to signal when done                */
  93. } NameFromLockMsg;
  94.  
  95. /*
  96.  *        This structure is used to communicate between the patches and
  97.  *        the background SnoopDos process when doing pattern matching.
  98.  */
  99. typedef struct PatternMsg {
  100.     struct Message    msg;        /* Standard exec message                */
  101.     int                type;        /* Type of this message                    */
  102.     char            *name;        /* Name of task to check                */
  103.     int                match;        /* Result of pattern match (1 == okay)    */
  104.     int                sigmask;    /* Signal number to signal when done    */
  105.     Task            *task;        /* Task to signal when done                */
  106. } PatternMsg;
  107.  
  108. /*
  109.  *        This structure is used to cache the results of pattern comparisons
  110.  *        for various tasks.
  111.  */
  112. typedef struct PatternCache {
  113.     struct MinNode    node;        /* Used to link items together                */
  114.     Task            *task;        /* ID of task stored in this entry            */
  115.     char            name[PC_NAMELEN];/* Name of task stored in this entry    */
  116.     int                match;        /* True=monitor this task, false=don't        */
  117. } PatternCache;
  118.  
  119. PatternCache    PCacheEntries[NUM_PCACHE_ENTRIES];    /* Entries on list        */
  120. MsgPort            BackgroundPort;    /* Where to send pattern match requests to    */
  121. List            PatternList;    /* List of cached patterns                    */
  122. Semaphore        PatternCacheSem;/* Sem to control access to pattern cache      */
  123. Semaphore        PatternBufSem;    /* Sem to control access to pattern buffer    */
  124. Semaphore        TaskCacheSem;    /* Sem used to arbitrate cache access        */
  125. Semaphore        DosDeviceSem;    /* Sem used to control access to dev list    */
  126. Process            *BackgroundProc;/* Process used to do pattern matching        */
  127. ULONG            PatternEnabled;        /* If true, pattern matching is enabled    */
  128. ULONG            PatternWildcard;    /* If true, pattern contains a wildcard    */
  129. Task            *SnoopTask;            /* Pointer to our own task                */
  130. Task            *RamLibTask;        /* Pointer to RamLib process            */
  131. Task            *RealRamLibTask;    /* Pointer to Ramlib process (always!)    */
  132. Task            *InputTask;            /* Pointer to input.device's task        */
  133. ULONG            NewEventSig    = -1;/* Signal bit used when new event ready    */
  134. ULONG            ScanDosListSig = -1;/* Signal bit used to say rescan list    */
  135. ULONG            TaskCacheIndex = 1; /* Index into task cache array            */
  136.  
  137. #define PAT_MAX_LEN    (MAX_STR_LEN   + 50)
  138. #define PAT_BUF_LEN    (PAT_MAX_LEN*2 + 3)
  139.  
  140. char            PatternString[PAT_MAX_LEN];            /* Holds source pattern    */
  141. char            PatternBuf[PAT_BUF_LEN];            /* Holds parsed pattern    */
  142. Task            *CachedTask[NUM_CACHED_TASKS];        /* Holds cached tasks    */
  143. Task            *DeviceTaskList[MAX_DOS_DEVICES];    /* Holds device IDs        */
  144.  
  145. /*
  146.  *        This structure records outstanding packets (for direct packet i/o
  147.  *        that bypasses AmigaDOS) which we are watching out for a reply to.
  148.  *        We keep all the additional info because it's likely that any given
  149.  *        task will re-use a packet structure when sending packets to different
  150.  *        DOS processes, and we want to be able to distinguish these cases.
  151.  */
  152. typedef struct WaitPacket {
  153.     struct    MinNode        node;            /* Used to link items together    */
  154.     struct    DosPacket    *dp;            /* Packet that was dispatched    */
  155.     struct    Task        *sendtask;        /* Task that sent it            */
  156.     struct    MsgPort        *destport;        /* Port it was sent to            */
  157.     LONG    eventnum;                    /* Associated event number        */
  158.     Event    *event;                        /* Associated event pointer        */
  159.     ULONG    arg1;                        /* ARG1 of the packet            */
  160.     ULONG    arg2;                        /* ARG2 of the packet            */
  161.     ULONG    arg3;                        /* ARG3 of the packet            */
  162.     char    *resmsg;                    /* Message to use for result    */
  163.     UWORD    flags;                        /* Flags associated with result    */
  164. } WaitPacket;
  165.  
  166. Semaphore    PacketSem;                            /* Arbitrates list access    */
  167. struct List PacketWaitList;                        /* List of waiting packets    */
  168. struct List    PacketFreeList;                        /* List of free nodes        */
  169. WaitPacket    PacketEntries[NUM_PACKET_ENTRIES];    /* The nodes on the list    */
  170.  
  171. /*
  172.  *        Prototypes for all our replacement patched functions
  173.  */
  174.  
  175. void BackgroundProcCode(void);
  176. char *MyNameFromLock(BPTR lock, char *filename, char *buf, int maxlen);
  177.  
  178. typedef unsigned long (*FuncPtr)();
  179.  
  180. #define FPROTO(name)    ULONG ASM New_##name(){return (0);}
  181. #define DPROTO(name)    ULONG ASM New_##name();
  182.  
  183. DPROTO(AddDosEntry)
  184. DPROTO(CurrentDir)
  185. DPROTO(DeleteFile)
  186. DPROTO(Execute)
  187. DPROTO(GetVar)
  188. DPROTO(FindVar)
  189. DPROTO(LoadSeg)
  190. DPROTO(NewLoadSeg)
  191. DPROTO(Lock)
  192. DPROTO(CreateDir)
  193. DPROTO(MakeLink)
  194. DPROTO(Open)
  195. DPROTO(Rename)
  196. DPROTO(RunCommand)
  197. DPROTO(SetVar)
  198. DPROTO(DeleteVar)
  199. DPROTO(SystemTagList)
  200.  
  201. DPROTO(FindPort)
  202. DPROTO(FindResident)
  203. DPROTO(FindSemaphore)
  204. DPROTO(FindTask)
  205. DPROTO(OpenDevice)
  206. DPROTO(OpenLibrary)
  207. DPROTO(OpenResource)
  208. DPROTO(PutMsg)
  209.  
  210. DPROTO(OpenFont)
  211. DPROTO(LockPubScreen)
  212. DPROTO(FindToolType)
  213. DPROTO(MatchToolValue)
  214.  
  215. /*
  216.  *        These two defines control whether or not a patch is enabled. It's
  217.  *        vital that PATCH_ENABLED be -1 rather than simply any non-zero
  218.  *        value -- see the comments in patchcode.s for more details.
  219.  */
  220. #define PATCH_ENABLED        ((ULONG)(-1))
  221. #define PATCH_DISABLED        0
  222.  
  223. /*
  224.  *        MarkCallAddr
  225.  *        CallAddr
  226.  *
  227.  *        These two macros allow us to determine what address we were called
  228.  *        from in a patch function. MarkCallAddr should be the first
  229.  *        declaration at the start of the patch function, and then CallAddr
  230.  *        will correspond to the caller's address throughout that function.
  231.  *        callmarker[0] is the place marker itself, callmarker[1] is the
  232.  *        immediate return address, and callmarker[3] takes into the
  233.  *        account the assembly language patch stub as well.
  234.  */
  235. #define MarkCallAddr        ULONG volatile callmarker[1]
  236. #define CallAddr            (callmarker[6])
  237.  
  238. /*
  239.  *        JumpOrigFunc(retval)
  240.  *    
  241.  *        This macro lets us return and call the original function from the
  242.  *        resident patch code rather than from our C code. The passed
  243.  *        parameter can be 0 in most cases, but if the function we're using
  244.  *        this in had a formal parameter passed in reg_d0, then that
  245.  *        formal parameter must be used as the return value.
  246.  *
  247.  *        We implement this by patching the stack directly to adjust the
  248.  *        return address so that we arrive back at a second bit of code
  249.  *        instead of the first bit.
  250.  */
  251. #define JumpOrigFunc(retval) \
  252.     { callmarker[1] += STACKADJUST; \
  253.       return (ULONG)(retval); }
  254.  
  255. /*
  256.  *        The patch structure itself. Don't change the order of the first 
  257.  *        four fields without updating the code in PATCHCODE.S as well!
  258.  *
  259.  *        Note that we use a library number rather than a pointer to an
  260.  *        actual library, for convenience.
  261.  */
  262. typedef struct Patch {
  263.     UWORD    code[CODESIZE/2];        /* Assembly code to handle patch    */
  264.     FuncPtr    origfunc;                /* Original function pointer        */
  265.     FuncPtr    newfunc;                /* Replacement function pointer        */
  266.     ULONG    enabled;                /* If -1, patch currently active    */
  267.     void    *sysbase;                /* Points to ExecBase                */
  268.     ULONG    stackneeded;            /* #bytes free stack required        */
  269.     UWORD    usecount;                /* No. of tasks currently in code    */
  270.     UWORD    library;                /* Library to patch                    */
  271.     WORD    offset;                    /* Offset into library                */
  272.     WORD    pad;                    /* Keep structure longword-aligned    */
  273. } Patch;
  274.  
  275. #define PATCH_DEF(lib,func)    { lib, LVO_##func, (FuncPtr)New_##func }
  276. #define PATCH_END            { 0, 0, 0 }
  277.  
  278. /*
  279.  *        This structure is used to initialise the main Patch array in memory.
  280.  *
  281.  *        IMPORTANT -- these patches MUST be defined in exactly the same
  282.  *        order as the first entries in the GID_* defines in snoopdos.h
  283.  *        since those same GID_* values are used to toggle the patches
  284.  *        on and off.
  285.  */
  286. struct PatchInitTable {
  287.     UWORD    library;                /* Library number to patch            */
  288.     WORD    offset;                    /* Offset in that library            */
  289.     FuncPtr    newfunc;                /* Replacement function to call        */
  290. } PatchInit[] = {
  291.     PATCH_END,
  292.     PATCH_DEF(    EXEC_LIB,        FindPort        ),    /* GID_FINDPORT            */
  293.     PATCH_DEF(    EXEC_LIB,        FindResident    ),    /* GID_FINDRESIDENT        */
  294.     PATCH_DEF(    EXEC_LIB,        FindSemaphore    ),    /* GID_FINDSEMAPHORE    */
  295.     PATCH_DEF(    EXEC_LIB,        FindTask        ),    /* GID_FINDTASK            */
  296.     PATCH_DEF(    INTUITION_LIB,    LockPubScreen    ),    /* GID_LOCKSCREEN        */
  297.     PATCH_DEF(    EXEC_LIB,        OpenDevice        ),    /* GID_OPENDEVICE        */
  298.     PATCH_DEF(    GRAPHICS_LIB,    OpenFont        ),    /* GID_OPENFONT            */
  299.     PATCH_DEF(    EXEC_LIB,        OpenLibrary        ),    /* GID_OPENLIBRARY        */
  300.     PATCH_DEF(    EXEC_LIB,        OpenResource    ),    /* GID_OPENRESOURCE        */
  301.     PATCH_DEF(    ICON_LIB,        FindToolType    ),    /* GID_READTOOLTYPES    */
  302.     PATCH_DEF(  EXEC_LIB,        PutMsg          ),    /* GID_SENDREXX            */
  303.  
  304.     PATCH_DEF(    DOS_LIB,        CurrentDir        ),    /* GID_CHANGEDIR        */
  305.     PATCH_DEF(    DOS_LIB,        DeleteFile        ),    /* GID_DELETE            */
  306.     PATCH_DEF(    DOS_LIB,        Execute            ),    /* GID_EXECUTE            */
  307.     PATCH_DEF(    DOS_LIB,        GetVar            ),    /* GID_GETVAR            */
  308.     PATCH_DEF(    DOS_LIB,        LoadSeg            ),    /* GID_LOADSEG            */
  309.     PATCH_DEF(    DOS_LIB,        Lock            ),    /* GID_LOCKFILE            */
  310.     PATCH_DEF(    DOS_LIB,        CreateDir        ),    /* GID_MAKEDIR            */
  311.     PATCH_DEF(    DOS_LIB,        MakeLink        ),    /* GID_MAKELINK            */
  312.     PATCH_DEF(    DOS_LIB,        Open            ),    /* GID_OPENFILE            */
  313.     PATCH_DEF(    DOS_LIB,        Rename            ),    /* GID_RENAME            */
  314.     PATCH_DEF(    DOS_LIB,        RunCommand        ),    /* GID_RUNCOMMAND        */
  315.     PATCH_DEF(    DOS_LIB,        SetVar            ),    /* GID_SETVAR            */
  316.     PATCH_DEF(    DOS_LIB,        SystemTagList    ),    /* GID_SYSTEM            */
  317.  
  318.     /*
  319.      *        Now the paired functions that track their partner's state
  320.      */
  321.     PATCH_DEF(    ICON_LIB,        MatchToolValue    ),    /* GID_READTOOLTYPES2    */
  322.     PATCH_DEF(    DOS_LIB,        NewLoadSeg        ),    /* GID_LOADSEG2            */
  323.     PATCH_DEF(    DOS_LIB,        FindVar           ),    /* GID_GETVAR2             */
  324.     PATCH_DEF(    DOS_LIB,        DeleteVar         ),    /* GID_SETVAR2          */
  325.     PATCH_DEF(  DOS_LIB,        AddDosEntry        ),    /* GID_ADDDOSENTRY        */
  326.  
  327.     PATCH_END
  328. };
  329.  
  330. #define PatchInitCount    (sizeof(PatchInit) / sizeof(PatchInit[0]))
  331. #define PatchInitSize    (PatchInitCount * sizeof(Patch))
  332.  
  333. /*
  334.  *        This anchor structure is used to let us locate the patches if we
  335.  *        quit SnoopDos and then rerun it -- the patches themselves always
  336.  *        stay in memory once loaded.
  337.  */
  338. typedef struct {
  339.     Semaphore    sem;            /* Semaphore to arbitrate access to patches    */
  340.     char        name[30];        /* Somewhere permanent to store sem name    */
  341.     Patch        patchdata[1];    /* This is an open-ended array                */
  342. } PatchAnchorData;
  343.  
  344. PatchAnchorData *PatchAnchor;    /* Points to the current patch anchor        */
  345.  
  346. /*
  347.  *        If SAS/C allowed us to examine the value of Enum types from
  348.  *        within the preprocessor, the following check would actually work
  349.  */
  350. #if SASC_ALLOWS_ENUM_CHECKS
  351. #if (sizeof(PatchInit)/sizeof(PatchInit[0])) < (GID_NUMPATCHES+1)
  352. #error "PatchInit[] table has too few entries for patches"
  353. #else
  354. #if (sizeof(PatchInit)/sizeof(PatchInit[0])) > (GID_NUMPATCHES+1)
  355. #error "PatchInit[] table has too many entries for patches"
  356. #endif
  357. #endif
  358. #endif
  359.  
  360. void *LibList[NUM_LIBS];
  361.  
  362. Patch *PatchList;
  363.  
  364. /*
  365.  *        Now we have a big table of DOS packets that we recognise while
  366.  *        monitoring. For each packet, we record the internal AmigaDOS ID
  367.  *        we use to recognise it, the message ID of its name, the number
  368.  *        of dp_Args to display, and the number of results to display.
  369.  */
  370.  
  371. /*        The following constants define how many parameters are returned
  372.  *        by the handler when it receives this packet. PK_0 means there is
  373.  *        no return value, PK_1 means 1 return value, PK_2 means there are
  374.  *        two return values (with the second being the error code if the
  375.  *        first one indicates failure) and PK_2OK means there are two actual
  376.  *        returned values, both of which are legitimate.
  377.  */
  378. #define PK_0            0            /* No return value from this packet        */
  379. #define PK_1            1            /* One return value from this packet    */
  380. #define PK_2            2            /* Two return values from this packet    */
  381. #define PK_2OK            3            /* Two return values even when okay        */
  382. #define PK_MASK            3            /* Used to extract PK_CODE                */
  383.  
  384. /*
  385.  *        How to detect when an event failed. Selecting none of these means
  386.  *        that the packet can never fail.
  387.  */
  388. #define PKF_BOOL        0x08        /* True if zero result == fail            */
  389. #define PKF_NEG            0x10        /* True if -1 result == fail            */
  390.  
  391. /*
  392.  *        We want to completely ignore some packets, namely all those sent
  393.  *        by the handler to lower level devices like timer.device or scsi.device
  394.  *        for its own needs. We use PK_IGNORE to identify all such packets
  395.  *        so we can discard them.
  396.  *
  397.  *        PK_COMMON packets are those which are monitored when the
  398.  *        "Monitor Packets" option is enabled. These are connected
  399.  *        with the associated DOS functions Open(), Lock() etc. and
  400.  *        are output as such by SnoopDos, rather than as raw packets.
  401.  *
  402.  *        PK_RAW is used for our sentinel packet which indicates that
  403.  *        even the packet type should be output since it's not recognised.
  404.  */
  405. #define PK_COMMON        0x20        /* Common packet (Open, Lock, etc.)        */
  406. #define PK_RAW            0x40        /* Completely raw packet                */
  407. #define PK_IGNORE        0x80        /* Ignore this packet completely        */
  408.  
  409. /*
  410.  *        Most packets return a value in dp_Res1 and if that value is
  411.  *        zero or -1, then the error code is in dp_Res2. We define
  412.  *        two constants to handle these common case.
  413.  */
  414. #define PK_BOOLEAN        (PKF_BOOL | PK_2)
  415. #define PK_NEGATIVE        (PKF_NEG  | PK_2)
  416.  
  417. /*
  418.  *        Warning: this table MUST be sorted by packet ID in ascending order,
  419.  *        since a binary search is used to locate packet types
  420.  */
  421. #define LAST_PACK_MSG        0
  422.  
  423. struct PacketRef {
  424.     UWORD    packetid;                /* The ID we watch out for                */
  425.     UWORD    msgid;                    /* The name of the action to print        */
  426.     UBYTE    numparams;                /* Number of parameters to display        */
  427.     UBYTE    flags;                    /* How to interpret the result            */
  428. } PacketTable[] = {
  429.     ACTION_STARTUP,            MSG_ACT_STARTUP,         1, PK_1,
  430.     ACTION_GET_BLOCK,        MSG_ACT_GET_BLOCK,         1, PK_IGNORE,
  431.     ACTION_SET_MAP,            MSG_ACT_SET_MAP,         1, PK_IGNORE,
  432.     ACTION_DIE,                MSG_ACT_DIE,             0, PK_BOOLEAN,
  433.     ACTION_EVENT,            MSG_ACT_EVENT,             1, PK_IGNORE,
  434.     ACTION_CURRENT_VOLUME,    MSG_ACT_CURRENT_VOLUME,     1, PK_2OK,
  435.     ACTION_LOCATE_OBJECT,    MSG_ACT_LOCATE_OBJECT,     3, PK_BOOLEAN | PK_COMMON,
  436.     ACTION_RENAME_DISK,        MSG_ACT_RENAME_DISK,     1, PK_BOOLEAN,
  437.     ACTION_FREE_LOCK,        MSG_ACT_FREE_LOCK,         1, PK_BOOLEAN,
  438.     ACTION_DELETE_OBJECT,    MSG_ACT_DELETE_OBJECT,     2, PK_BOOLEAN | PK_COMMON,
  439.     ACTION_RENAME_OBJECT,    MSG_ACT_RENAME_OBJECT,     4, PK_BOOLEAN | PK_COMMON,
  440.     ACTION_MORE_CACHE,        MSG_ACT_MORE_CACHE,         1, PKF_BOOL   | PK_2OK,
  441.     ACTION_COPY_DIR,        MSG_ACT_COPY_DIR,         1, PK_BOOLEAN,
  442.     ACTION_WAIT_CHAR,        MSG_ACT_WAIT_CHAR,         1, PKF_BOOL   | PK_2OK,
  443.     ACTION_SET_PROTECT,        MSG_ACT_SET_PROTECT,     4, PK_BOOLEAN,
  444.     ACTION_CREATE_DIR,        MSG_ACT_CREATE_DIR,         2, PK_BOOLEAN | PK_COMMON,
  445.     ACTION_EXAMINE_OBJECT,    MSG_ACT_EXAMINE_OBJECT,     2, PK_BOOLEAN,
  446.     ACTION_EXAMINE_NEXT,    MSG_ACT_EXAMINE_NEXT,     2, PK_BOOLEAN,
  447.     ACTION_DISK_INFO,        MSG_ACT_DISK_INFO,         1, PK_BOOLEAN,
  448.     ACTION_INFO,            MSG_ACT_INFO,             2, PK_BOOLEAN,
  449.     ACTION_FLUSH,            MSG_ACT_FLUSH,             0, PK_BOOLEAN,
  450.     ACTION_SET_COMMENT,        MSG_ACT_SET_COMMENT,     4, PK_BOOLEAN,
  451.     ACTION_PARENT,            MSG_ACT_PARENT,             1, PK_BOOLEAN,
  452.     ACTION_TIMER,            MSG_ACT_TIMER,             1, PK_IGNORE,
  453.     ACTION_INHIBIT,            MSG_ACT_INHIBIT,         1, PK_BOOLEAN,
  454.     ACTION_DISK_TYPE,        MSG_ACT_DISK_TYPE,         1, PK_IGNORE,
  455.     ACTION_DISK_CHANGE,        MSG_ACT_DISK_CHANGE,     1, PK_1,
  456.     ACTION_SET_DATE,        MSG_ACT_SET_DATE,         4, PK_BOOLEAN,
  457.     ACTION_SAME_LOCK,        MSG_ACT_SAME_LOCK,         2, PK_BOOLEAN,
  458.     ACTION_READ,            MSG_ACT_READ,             3, PK_NEGATIVE,
  459.     ACTION_WRITE,            MSG_ACT_WRITE,             3, PK_NEGATIVE,
  460.     ACTION_SCREEN_MODE,        MSG_ACT_SCREEN_MODE,     1, PK_BOOLEAN,
  461.     ACTION_CHANGE_SIGNAL,    MSG_ACT_CHANGE_SIGNAL,     3, PKF_BOOL   | PK_2OK,
  462.     ACTION_READ_RETURN,        MSG_ACT_READ_RETURN,     1, PK_IGNORE,
  463.     ACTION_WRITE_RETURN,    MSG_ACT_WRITE_RETURN,     1, PK_IGNORE,
  464.     ACTION_FINDUPDATE,        MSG_ACT_FINDUPDATE,         3, PK_BOOLEAN | PK_COMMON,
  465.     ACTION_FINDINPUT,        MSG_ACT_FINDINPUT,         3, PK_BOOLEAN | PK_COMMON,
  466.     ACTION_FINDOUTPUT,        MSG_ACT_FINDOUTPUT,         3, PK_BOOLEAN | PK_COMMON,
  467.     ACTION_END,                MSG_ACT_END,             1, PK_BOOLEAN,
  468.     ACTION_SEEK,            MSG_ACT_SEEK,             3, PK_NEGATIVE,
  469.     ACTION_FORMAT,            MSG_ACT_FORMAT,             2, PK_BOOLEAN,
  470.     ACTION_MAKE_LINK,        MSG_ACT_MAKE_LINK,         4, PK_BOOLEAN | PK_COMMON,
  471.     ACTION_SET_FILE_SIZE,    MSG_ACT_SET_FILE_SIZE,     3, PK_NEGATIVE,
  472.     ACTION_WRITE_PROTECT,    MSG_ACT_WRITE_PROTECT,     2, PK_BOOLEAN,
  473.     ACTION_READ_LINK,        MSG_ACT_READ_LINK,         4, PK_BOOLEAN,
  474.     ACTION_FH_FROM_LOCK,    MSG_ACT_FH_FROM_LOCK,     2, PK_BOOLEAN,
  475.     ACTION_IS_FILESYSTEM,    MSG_ACT_IS_FILESYSTEM,     0, PK_BOOLEAN,
  476.     ACTION_CHANGE_MODE,        MSG_ACT_CHANGE_MODE,     3, PK_BOOLEAN,
  477.     ACTION_COPY_DIR_FH,        MSG_ACT_COPY_DIR_FH,     1, PK_BOOLEAN,
  478.     ACTION_PARENT_FH,        MSG_ACT_PARENT_FH,         1, PK_BOOLEAN,
  479.     ACTION_EXAMINE_ALL,        MSG_ACT_EXAMINE_ALL,     5, PK_BOOLEAN,
  480.     ACTION_EXAMINE_FH,        MSG_ACT_EXAMINE_FH,         2, PK_BOOLEAN,
  481.     ACTION_EXAMINE_ALL_END,    MSG_ACT_EXAMINE_ALL_END, 5, PK_BOOLEAN,
  482.     ACTION_SET_OWNER,        MSG_ACT_SET_OWNER,         4, PK_BOOLEAN,
  483.     ACTION_DOUBLE,            MSG_ACT_DOUBLE,             3, PK_BOOLEAN,
  484.     ACTION_FORCE,            MSG_ACT_FORCE,             3, PK_BOOLEAN,
  485.     ACTION_STACK,            MSG_ACT_STACK,             3, PK_BOOLEAN,
  486.     ACTION_QUEUE,            MSG_ACT_QUEUE,             3, PK_BOOLEAN,
  487.     ACTION_DROP,            MSG_ACT_DROP,             1, PK_BOOLEAN,
  488.     ACTION_LOCK_RECORD,        MSG_ACT_LOCK_RECORD,     5, PK_BOOLEAN,
  489.     ACTION_FREE_RECORD,        MSG_ACT_FREE_RECORD,     3, PK_BOOLEAN,
  490.     ACTION_ADD_NOTIFY,        MSG_ACT_ADD_NOTIFY,         1, PK_BOOLEAN,
  491.     ACTION_REMOVE_NOTIFY,    MSG_ACT_REMOVE_NOTIFY,     1, PK_BOOLEAN,
  492.     ACTION_SERIALIZE_DISK,    MSG_ACT_SERIALIZE_DISK,     0, PK_BOOLEAN,
  493.     ACTION_GET_DISK_FSSM,    MSG_ACT_GET_DISK_FSSM,     0, PK_BOOLEAN,
  494.     ACTION_FREE_DISK_FSSM,    MSG_ACT_FREE_DISK_FSSM,     0, PK_BOOLEAN,
  495.     /*
  496.       *        Our final table entry is used as a sentinel -- we key off the
  497.      *        final packet message as an indication that we're finished
  498.      *        searching the table.
  499.       */
  500.     0,                        LAST_PACK_MSG,             4, PK_2OK | PK_RAW
  501. };
  502.  
  503. #define NUM_PACKETS        (sizeof(PacketTable) / sizeof(PacketTable[0]))
  504.  
  505. /*****************************************************************************
  506.  *
  507.  *        Start of functions!
  508.  *
  509.  *****************************************************************************/
  510.  
  511. /*
  512.  *        InitPatches()
  513.  *
  514.  *        Sets up the library pointers and makes sure all the patch
  515.  *        code vectors are initialised properly.
  516.  *
  517.  *        Also allocates memory for the patches (if necessary)
  518.  *
  519.  *        Returns TRUE for success, FALSE for failure.
  520.  */
  521. int InitPatches(void)
  522. {
  523.     struct PatchInitTable *pi;
  524.     Patch *p;
  525.     int i;
  526.  
  527.     /*
  528.      *        Before doing anything else, do a quick check to ensure that
  529.      *        the patch code in PATCHCODE.S agrees with the equivalent patch
  530.      *        structure defined in this file.
  531.      */
  532.     if (CODESIZE != PatchCode_Size || STACKADJUST != PatchCode_StackAdjust) {
  533.         Printf("In patches.c, adjust CODESIZE by %ld and STACKADJUST by %ld\n",
  534.                 PatchCode_Size-CODESIZE, PatchCode_StackAdjust-STACKADJUST);
  535.         return (FALSE);
  536.     }
  537.  
  538.     LibList[DOS_LIB]        = DOSBase;
  539.     LibList[EXEC_LIB]        = SysBase;
  540.     LibList[INTUITION_LIB]    = IntuitionBase;
  541.     LibList[GRAPHICS_LIB]    = GfxBase;
  542.     LibList[ICON_LIB]        = IconBase;
  543.  
  544.     /*
  545.      *        First, let's calculate our ROM start and end address.
  546.      *        We do this by getting the address pointed to by the
  547.      *        Open LVO vector in exec.library and rounding it down
  548.      *        to the nearest 512K
  549.      */
  550.     RomStart = (*((ULONG *)(((char *)SysBase) + LIB_OPEN + 2))) & 0xFFF80000;
  551.     RomEnd   = RomStart + 0x7FFFF;
  552.  
  553.     /*
  554.      *        Now see if the patches were already installed by an earlier
  555.      *        version of SnoopDos.
  556.      */
  557.     Forbid();
  558.     PatchAnchor = (PatchAnchorData *)FindSemaphore(PATCHES_NAME);
  559.     if (!PatchAnchor) {
  560.         /*
  561.          *        Patches weren't found, so allocate a new set and initialise
  562.          *        it with our patch code.
  563.          */
  564.         PatchAnchor = AllocMem(sizeof(PatchAnchorData) + PatchInitSize,
  565.                                MEMF_ANY | MEMF_CLEAR);
  566.         if (!PatchAnchor) {
  567.             Permit();
  568.             return (FALSE);
  569.         }
  570.         strcpy(PatchAnchor->name, PATCHES_NAME);
  571.         PatchAnchor->sem.ss_Link.ln_Name = PatchAnchor->name;
  572.         PatchAnchor->sem.ss_Link.ln_Pri  = 0;
  573.         AddSemaphore(&PatchAnchor->sem);
  574.     }
  575.     /*
  576.      *        Now PatchAnchor points to our patch list in memory -- copy over
  577.      *        our patch code and initialised data accordingly.
  578.      */
  579.     PatchList = PatchAnchor->patchdata;
  580.     for (pi = PatchInit+1, p = PatchList+1; pi->offset; p++, pi++) {
  581.         memcpy(p->code, PatchCode_Start, CODESIZE);
  582.         p->library        = pi->library;
  583.         p->offset        = pi->offset;
  584.         p->newfunc        = pi->newfunc;
  585.         p->stackneeded    = CurSettings.StackLimit;
  586.         p->sysbase        = SysBase;
  587.     }
  588.     CacheClearU();    /* Invalidate cache for code we just built */
  589.     Permit();
  590.  
  591.     /*
  592.      *        Now initialise other variables associated with the patch code
  593.      */
  594.     SnoopTask    = SysBase->ThisTask;
  595.     InputTask    = FindTask("input.device");
  596.     InitRamLibPatch();
  597.     InitSemaphore(&PatternBufSem);
  598.     InitSemaphore(&PatternCacheSem);
  599.     InitSemaphore(&TaskCacheSem);
  600.     InitSemaphore(&DosDeviceSem);
  601.     InitSemaphore(&PauseSem);
  602.     InitSemaphore(&PacketSem);
  603.  
  604.     UpdateDeviceList(SCANDEV_IMMEDIATE);
  605.  
  606.     NewList(&PatternList);
  607.     for (i = 0; i < NUM_PCACHE_ENTRIES; i++)
  608.         AddHead(&PatternList, (Node *)&PCacheEntries[i]);
  609.     
  610.     NewList(&PacketWaitList);
  611.     NewList(&PacketFreeList);
  612.     for (i = 0; i < NUM_PACKET_ENTRIES; i++)
  613.         AddHead(&PacketFreeList, (Node *)&PacketEntries[i]);
  614.  
  615.     NewEventSig = AllocSignal(-1);
  616.     if (NewEventSig == -1)
  617.         return (FALSE);
  618.     NewEventMask = 1 << NewEventSig;
  619.  
  620.     /*
  621.      *        Signal bit used to tell main task to rescan device list because
  622.      *        a device was added (or removed?)
  623.      */
  624.     ScanDosListSig = AllocSignal(-1);
  625.     if (ScanDosListSig == -1)
  626.         return (FALSE);
  627.     ScanDosListMask = 1 << ScanDosListSig;
  628.  
  629.     /*
  630.      *        Now create a background process to handle process matching.
  631.      *        We run this at a high priority to ensure quick response.
  632.      *
  633.      *        We first of all initialise our pattern port, and set it
  634.      *        to NOT signal our task when a message arrives. This ensures
  635.      *        that if, for some reason, our new process doesn't run
  636.      *        until after someone tries to send it a message, nothing
  637.      *        bad will happen (e.g. signalling a task at 0x0000)
  638.      *        
  639.      *        The background process itself will replace this with
  640.      *        PA_SIGNAL to make things work normally again.
  641.      */
  642.     NewList(&BackgroundPort.mp_MsgList);
  643.     BackgroundPort.mp_Flags = PA_IGNORE;
  644.     BackgroundProc = CreateNewProcTags(NP_Entry,     BackgroundProcCode,
  645.                                           NP_Name,      BACKGROUND_NAME,
  646.                                        NP_Priority,  5,
  647.                                           TAG_DONE);
  648.  
  649.     return (BackgroundProc != NULL);
  650. }
  651.  
  652. /*
  653.  *        UpdatePatches()
  654.  *
  655.  *        Scans the list of patches, updating our behaviour as follows.
  656.  *        This means:
  657.  *
  658.  *            - Any patch that's currently active and which has been flagged
  659.  *              as inactive should be disabled if possible (i.e. origfunc
  660.  *              set to NULL).
  661.  *
  662.  *            - Any patch that's currently inactive and which has been flagged
  663.  *              as active should be enabled if possible (i.e. origfunc set
  664.  *              to point to the original function).
  665.  *
  666.  *        If we are currently Disabled, then all patches are made inactive.
  667.  *        
  668.  */
  669. void UpdatePatches(void)
  670. {
  671.     Patch *p;
  672.  
  673.     if (!PatchList)
  674.         return;
  675.  
  676.     if (MonPackets || ShowAllPackets)
  677.         UpdateDeviceList(SCANDEV_IMMEDIATE); /* Keep device list up to date */
  678.  
  679.     Forbid();            /* Don't even think about letting anyone else run! */
  680.  
  681.     /*
  682.      *        Update status of pair functions to reflect the master
  683.      *        function's status
  684.      */
  685.     PatchList[GID_READTOOLTYPES2].enabled    =
  686.                             PatchList[GID_READTOOLTYPES].enabled;
  687.     PatchList[GID_LOADSEG2].enabled            =
  688.                             PatchList[GID_LOADSEG].enabled;
  689.     PatchList[GID_GETVAR2].enabled            =
  690.                             PatchList[GID_GETVAR].enabled;
  691.     PatchList[GID_SETVAR2].enabled            =
  692.                             PatchList[GID_SETVAR].enabled;
  693.  
  694.     for (p = PatchList+1; p->offset; p++) {
  695.         void *lib        = LibList[p->library];
  696.         int func_enabled = (Disabled ? PATCH_DISABLED : p->enabled);
  697.  
  698.         p->stackneeded     = CurSettings.StackLimit;    /* Update this */
  699.  
  700.         if (!lib)            /* Skip over any libraries we couldn't open */
  701.             continue;
  702.  
  703.         if (p->origfunc == NULL && func_enabled != PATCH_DISABLED) {
  704.             /*
  705.              *        Okay, we want to enable this function.
  706.              */
  707.             p->origfunc = (FuncPtr)SetFunction(lib, p->offset, (FuncPtr)p);
  708.         } else if (p->origfunc && func_enabled == PATCH_DISABLED) {
  709.             /*
  710.              *        Okay, we want to disable this function. However, we
  711.              *        may not be able to do this cleanly, if it's been
  712.              *        since patched by someone else. Thus, if our attempt
  713.              *        to unpatch it doesn't return a pointer to our replacement
  714.              *        code, we have to leave the patch installed. However, it
  715.              *        will be re-tried automatically the next time someone
  716.              *        calls this function.
  717.              */
  718.             FuncPtr curfunc = (FuncPtr)SetFunction(lib, p->offset, p->origfunc);
  719.  
  720.             if (curfunc != (FuncPtr)p) {
  721.                 /*
  722.                  *        Uhoh -- someone else patched us!
  723.                  */
  724.                 SetFunction(lib, p->offset, (FuncPtr)curfunc);
  725.             } else {
  726.                 /*
  727.                  *        We unpatched successfully, so zero pointer
  728.                  */
  729.                 p->origfunc = NULL;
  730.             }
  731.         }
  732.     }
  733.     /*
  734.      *        We disable stack monitoring for the PutMsg() patch because
  735.      *        we need to catch the return packet from small-stack processes
  736.      *        like RAM: etc. Instead, that patch does its own stack checking.
  737.      */
  738.     PatchList[GID_SENDREXX].stackneeded = 0;
  739.  
  740.     CacheClearU();    /* Just to be safe */
  741.     Permit();
  742.     /*
  743.      *        Finally, check if we've disabled packet monitoring and
  744.      *        there are some outstanding packets that have not yet had
  745.      *        return values assigned. Free them all as appropriate
  746.      */
  747.     if (!MonPackets && !ShowAllPackets)
  748.     {
  749.         for (;;) {
  750.             WaitPacket *wp;
  751.             Event *ev;
  752.             LONG  seqnum;
  753.  
  754.             ObtainSemaphore(&PacketSem);
  755.             if (IsListEmpty(&PacketWaitList)) {
  756.                 ReleaseSemaphore(&PacketSem);
  757.                 break;
  758.             }
  759.             wp        = (WaitPacket *)RemHead(&PacketWaitList);
  760.             AddHead(&PacketFreeList, (Node *)wp);
  761.             ev       = wp->event;
  762.             seqnum = wp->eventnum;
  763.             ReleaseSemaphore(&PacketSem);
  764.  
  765.             ObtainSemaphore(&BufSem);
  766.             if (seqnum >= RealFirstSeq) {
  767.                 ev->status = ES_READY;
  768.                 Signal(SnoopTask, NewEventMask);
  769.             }
  770.             ReleaseSemaphore(&BufSem);
  771.         }
  772.     }
  773. }
  774.  
  775. /*
  776.  *        CleanupPatches()
  777.  *
  778.  *        Frees any resources allocated when initialising this module. Also
  779.  *        disable any patches that are still active.
  780.  */
  781. void CleanupPatches(void)
  782. {
  783.     static int unpatchtries = 0;
  784.  
  785.     if (PatchList) {
  786.         struct PatchInitTable *pi;
  787.         Patch *p;
  788.  
  789.         for (p = PatchList+1; p->offset; p++)
  790.             p->enabled = PATCH_DISABLED;
  791.         UpdatePatches();
  792.  
  793.         /*
  794.          *        Now our patches have been disabled, and it's time to
  795.          *        ensure nobody got left inside our code. We do this by
  796.          *        checking that all the usecounts are set to zero, and if
  797.          *        they're not, giving the user the option of continuing
  798.          *        to try, or exiting.
  799.          *
  800.          *        Before we do this, however, we record all the functions
  801.          *        which still have a usecount > 0 -- this way, if someone
  802.          *        else runs SnoopDos while we are checking and installs
  803.          *        a new set of patches in the patchtable, and enables
  804.          *        some additional functions, we don't have to check that
  805.          *        ALL usage counts are zero, only those which were non-zero
  806.          *        at the start of the loop.
  807.          *
  808.          *        (That is, we ignore any usage counts which are results of
  809.          *        functions being re-enabled by the new SnoopDos -- typically,
  810.          *        there will only be one or two functions at most to be
  811.          *        checked.)
  812.          *
  813.          *        For convenience, we re-use the PatchInit list to hold the
  814.          *        exit "usage" states for each function -- we won't be needing
  815.          *        it any more.
  816.          */
  817.         for (p = (PatchList+1), pi = (PatchInit+1); p->offset; p++, pi++)
  818.             pi->offset = p->usecount;
  819.  
  820.         for (;;) {
  821.             for (p = (PatchList+1), pi = (PatchInit+1); p->offset; p++, pi++) {
  822.                 if (pi->offset > 0) {
  823.                     if (p->usecount > 0)
  824.                         break;
  825.                     pi->offset = 0;    /* No longer active, so mark as done */
  826.                 }
  827.             }
  828.             if (p->offset) {
  829.                 /*
  830.                  *        Couldn't unpatch, so go around the loop and try
  831.                  *        again. After about 8-10 seconds, we warn the user
  832.                  *        that we're still trying.
  833.                  */
  834.                 unpatchtries++;
  835.                 if (unpatchtries == 5) {
  836.                     char errorbuf[400];
  837.                     
  838.                     mysprintf(errorbuf, MSG(MSG_ERROR_UNPATCH),
  839.                               GetFuncName(p - PatchList));
  840.                     ShowError(errorbuf);
  841.                 }
  842.                 Delay(2*50);        /* Wait 2 seconds before next try */
  843.             } else
  844.                 break;
  845.         }
  846.     }
  847.     if (BackgroundProc) {
  848.         /*
  849.          *        Send a terminate message to the pattern process and wait
  850.          *        for it to respond
  851.          */
  852.         PatternMsg pmsg;
  853.  
  854.         pmsg.type                = QUIT_MSG;
  855.         pmsg.sigmask             = SIGF_SINGLE;
  856.         pmsg.task                 = SysBase->ThisTask;
  857.         pmsg.msg.mn_Node.ln_Name = NULL;
  858.  
  859.         SetSignal(0, SIGF_SINGLE);        /* Ensure signal is clear first */
  860.         PutMsg(&BackgroundPort, &pmsg);    /* Send quit message to process    */
  861.         Wait(SIGF_SINGLE);                /* Wait for acknowledgement        */
  862.     }
  863.     if (NewEventSig    != -1) FreeSignal(NewEventSig),    NewEventSig    = -1;
  864.     if (ScanDosListSig != -1) FreeSignal(ScanDosListSig), ScanDosListSig = -1;
  865. }
  866.  
  867. /*
  868.  *        InitRamLibPatch()
  869.  *
  870.  *        Attempts to patch the ramlib process so that it no longer uses
  871.  *        a message port with the signal bit set to SIGB_SINGLE (which
  872.  *        is asking for trouble if you call any functions that use
  873.  *        semaphores, since a SIGB_SINGLE can be generated by a message
  874.  *        arriving at the message port while waiting for the semaphore,
  875.  *        causing all hell to break loose -- the semaphore appears to be
  876.  *        obtained while in fact it's still in use by someone else).
  877.  */
  878. void InitRamLibPatch(void)
  879. {
  880.     unsigned char *ramdata;
  881.     int i;
  882.     
  883.     RealRamLibTask = FindTask("ramlib");
  884.     if (NoPatchRamLib || !RealRamLibTask) {
  885.         /*
  886.          *        Not patching ramlib, so instead, install our bypass patch
  887.          *        that stops us from monitoring any ramlib tasks so that
  888.          *        we won't get crashed.
  889.          */
  890.         RamLibTask = RealRamLibTask;
  891.     } else {
  892.         /*
  893.          *        Patch ramlib itself so that it doesn't use SIGF_SINGLE
  894.          *        any more.
  895.          */
  896.         ramdata = (void *)SysBase->ex_RamLibPrivate;
  897.         if (!ramdata) {
  898.             /*
  899.              *        You never know ... this field may no longer exist. If
  900.              *        it doesn't, then assume the bug no longer exists either
  901.              *        and exit.
  902.              */
  903.             return;
  904.         }
  905.         for (i = 0x20; i < 0x50; i += 2) {
  906.             struct MsgPort *ramport = (struct MsgPort *)(ramdata + i);
  907.  
  908.             /*
  909.              *        Depending on the Kickstart version (V37 through V40)
  910.              *        the ramlib message port lies somewhere in the range
  911.              *        0x30 to 0x50 of the ramlib's private area. We do
  912.              *        some integrity checks on the port to make sure it's
  913.              *        not just a lucky hit. If some future SetPatch fixes
  914.              *        this, then we will never find a match, which suits
  915.              *        us fine.
  916.              */
  917.             if (ramport->mp_Flags   == 0            &&
  918.                 ramport->mp_SigBit  == SIGB_SINGLE    &&
  919.                 ramport->mp_SigTask == RealRamLibTask)
  920.             {
  921.                 /*
  922.                  *        Now patch ramlib's message port so it won't use
  923.                  *        SIGB_SINGLE any more. This is a little tricky, since
  924.                  *        ramlib process will be in the middle of WaitPort()
  925.                  *        and WaitPort() won't return until a message arrives at
  926.                  *        the port. Essentially, we can only change the port's
  927.                  *        message bit while we are sure that the ramlib process
  928.                  *        is NOT in a WaitPort(). We do this by bumping our
  929.                  *        process priority up to 127 temporarily, making the
  930.                  *        call, and then restoring it.
  931.                  *
  932.                  *        Since we're at a higher priority than ramlib, then
  933.                  *        as soon as ramlib responds to the message, it will
  934.                  *        be switched out (in the Ready state) before it has a
  935.                  *        chance to call WaitPort() and go back to sleep again.
  936.                  */
  937.                 ULONG oldpri;
  938.  
  939.                 Forbid();
  940.                 oldpri = SetTaskPri(SysBase->ThisTask, 127);
  941.                 OpenLibrary("ramlib-patch.library", 0);    /* Wake-up ramlib */
  942.                 ramport->mp_SigBit = SIGBREAKB_CTRL_E;
  943.                 SetTaskPri(SysBase->ThisTask, oldpri);
  944.                 Permit();
  945.                 break;
  946.             }
  947.         }
  948.     }
  949. }
  950.  
  951. /*
  952.  *        UpdateDeviceList(method)
  953.  *
  954.  *        Updates the DOS device list. This is used to keep an internal list
  955.  *        of all the task IDs of DOS handlers so we can quickly determine
  956.  *        if a given ID belongs to a handler or not.
  957.  *
  958.  *        This should be called at least once, and ideally every now and
  959.  *        again, so that new devices added to the DOS list can be detected
  960.  *        and old ones ignored.
  961.  *
  962.  *        The method can be SCANDEV_IMMEDIATE, to immediately scan the device
  963.  *        list, or SCANDEV_DELAY to wait for a second before updating. The
  964.  *        delay is because the patch that signals us to rescan (AddDosEntry)
  965.  *        happens before the device is actually fully initialised, so we need
  966.  *        to give it a little extra time to get going.
  967.  *
  968.  *        It's not critical if we don't give it enough time, it just means
  969.  *        that the new device won't appear in the list after all -- a bit
  970.  *        annoying for people using the packet debugger to help debug a
  971.  *        device they keep mounting interactively.
  972.  */
  973. void UpdateDeviceList(int method)
  974. {
  975.     struct DosList *dlist;
  976.     int i = 0;
  977.  
  978.     if (method == SCANDEV_DELAY)
  979.         Delay(50);
  980.  
  981.     ObtainSemaphore(&DosDeviceSem);
  982.     dlist = LockDosList(LDF_DEVICES | LDF_READ);
  983.     while ((dlist = NextDosEntry(dlist, LDF_DEVICES))
  984.              && i < (MAX_DOS_DEVICES-1))
  985.     {
  986.         if (dlist->dol_Task)
  987.             DeviceTaskList[i++] = dlist->dol_Task->mp_SigTask;
  988.     }
  989.     UnLockDosList(LDF_DEVICES | LDF_READ);
  990.     DeviceTaskList[i] = 0;
  991. #if 0
  992.     Printf("New device list:\n");
  993.     for (i = 0; DeviceTaskList[i]; i++)
  994.         Printf("    %2ld = %s\n", i, DeviceTaskList[i]->tc_Node.ln_Name);
  995. #endif
  996.     ReleaseSemaphore(&DosDeviceSem);
  997. }
  998.  
  999. /*
  1000.  *        SetPattern(string, ignorewb)
  1001.  *
  1002.  *        Sets the current pattern to the given string (possibly containing
  1003.  *        wildcards). If ignorewb is true, then the pattern is modified to
  1004.  *        also exclude any processes with the names Workbench, Shell Process
  1005.  *        or Background CLI.
  1006.  */
  1007. void SetPattern(char *pattern, int ignorewb)
  1008. {
  1009.     PatternCache *pc;
  1010.     char *p;
  1011.  
  1012.     if (*pattern == '\0' && !ignorewb) {
  1013.         PatternEnabled = 0;
  1014.         return;
  1015.     }
  1016.     ObtainSemaphore(&PatternCacheSem);
  1017.     ObtainSemaphore(&PatternBufSem);
  1018.     PatternEnabled = 0;
  1019.  
  1020.     /*
  1021.      *        Now, update our pattern string. If the pattern string is empty,
  1022.      *        we disable patterns. If the pattern string contains no wildcards,
  1023.      *        we enable patterns but disable wildcard patching (this is much
  1024.      *        faster, since it can be done in the patch's task, rather than
  1025.      *        having to call back to the main routine).
  1026.      *
  1027.      *        We allocate both pattern semaphores: the Cache semaphore to
  1028.      *        ensure that no pattern code can execute when PatternEnabled
  1029.      *        is zero, and the buffer semaphore to ensure that the pattern
  1030.      *        process doesn't try to do pattern matching while we are
  1031.      *        building the new pattern string.
  1032.      *
  1033.      *        We need to be careful to get the cache semaphore BEFORE we get
  1034.      *        the pattern buffer semaphore, otherwise we could deadlock if
  1035.      *        a patch decided to check a pattern at just the wrong time.
  1036.      */
  1037.     if (ignorewb) {
  1038.         if (*pattern == '\0')
  1039.             strcpy(PatternString, PAT_EASY_EXCLUDE);
  1040.         else
  1041.             mysprintf(PatternString, PAT_COMPLEX_EXCLUDE, pattern);
  1042.     } else
  1043.         strcpy(PatternString, pattern);
  1044.  
  1045.     /*
  1046.      *        Now, due to a bug in AmigaDOS's ParsePatternNoCase, we
  1047.      *        have to convert the string to upper case first. (The bug
  1048.      *        is that character classes like [a-z] will never match anything,
  1049.      *        only [A-Z] will work. Since it's case insensitive for
  1050.      *        everything else, we simply convert everything to upper case.)
  1051.      */
  1052.     for (p = PatternString; *p; p++) {
  1053.         if (*p >= 'a' && *p <= 'z')
  1054.             *p &= 0x5F;
  1055.     }
  1056.     PatternWildcard = ParsePatternNoCase(PatternString,
  1057.                                          PatternBuf, PAT_BUF_LEN);
  1058.  
  1059.     if (PatternWildcard != -1)    /* Only enable patterns if successful */
  1060.         PatternEnabled = 1;
  1061.     
  1062.     /*
  1063.      *        Regardless of what happened, invalidate the pattern cache
  1064.      *        to force a new match for each task name.
  1065.      */
  1066.     FORLIST(&PatternList, pc)
  1067.         pc->task = NULL;
  1068.  
  1069.     ReleaseSemaphore(&PatternBufSem);
  1070.     ReleaseSemaphore(&PatternCacheSem);
  1071. }
  1072.  
  1073. /*
  1074.  *        FlushWaitingPackets(void)
  1075.  *
  1076.  *        Flushes any packets currently in the waiting packets queue
  1077.  *        (marking them as missed). Usually called after packet monitoring
  1078.  *        has been disabled, in case there were any half-completed packets
  1079.  *        in the queue.
  1080.  */
  1081. void FlushWaitingPackets(void)
  1082. {
  1083.     WaitPacket *wp;
  1084.     int flushed = 0;
  1085.  
  1086.     ObtainSemaphore(&PacketSem);
  1087.     wp = HeadNode(&PacketWaitList);
  1088.     while (NextNode(wp)) {
  1089.         ULONG seqnum = wp->eventnum;
  1090.         Event *ev    = wp->event;
  1091.  
  1092.         Remove((Node *)wp);
  1093.         AddHead(&PacketFreeList, (Node *)wp);
  1094.         ReleaseSemaphore(&PacketSem);    /* Never lock two sem's at once! */
  1095.         ObtainSemaphore(&BufSem);
  1096.         if (seqnum >= RealFirstSeq) {
  1097.             ev->result = MSG(MSG_RES_MISSED);
  1098.             ev->status = ES_READY;
  1099.             flushed    = 1;
  1100.         }
  1101.         ReleaseSemaphore(&BufSem);
  1102.         ObtainSemaphore(&PacketSem);
  1103.         wp = HeadNode(&PacketWaitList);
  1104.     }
  1105.     ReleaseSemaphore(&PacketSem);
  1106.  
  1107.     if (flushed)
  1108.         Signal(SnoopTask, NewEventMask);
  1109. }
  1110.  
  1111. /*
  1112.  *        LoadFuncSettings(FuncSets)
  1113.  *
  1114.  *        Downloads the values in the passed-in FuncSettings array to the
  1115.  *        various local structures that require them.
  1116.  */
  1117. void LoadFuncSettings(FuncSettings *func)
  1118. {
  1119.     Patch *p;
  1120.     int i;
  1121.  
  1122.     if (!PatchList)
  1123.         return;
  1124.  
  1125.     Forbid();
  1126.     for (p = PatchList+1, i = 1; p->offset; p++, i++)
  1127.         p->enabled = ((func->Opts[i] && !Disabled) ?
  1128.                             PATCH_ENABLED : PATCH_DISABLED);
  1129.     /*
  1130.      *        A bit of a hack -- the PutMsg() function is needed to monitor
  1131.      *        three individual patches: SendRexx, Monitor Packets, and
  1132.      *        Packet Debugger. We do the check for the last two here.
  1133.      */
  1134.     if ((func->Opts[GID_MONPACKETS] || func->Opts[GID_MONALLPACKETS]) &&
  1135.          !Disabled)
  1136.     {
  1137.         PatchList[GID_SENDREXX].enabled    = PATCH_ENABLED;
  1138.         PatchList[GID_ADDDOSENTRY].enabled = PATCH_ENABLED;
  1139.     } else {
  1140.         PatchList[GID_ADDDOSENTRY].enabled = PATCH_DISABLED;
  1141.         FlushWaitingPackets();
  1142.     }
  1143.     UpdatePatches();
  1144.     Permit();
  1145.     SetPattern(func->Pattern, func->Opts[GID_IGNOREWB]);
  1146. }
  1147.  
  1148. /*
  1149.  *        BackgroundProcCode()
  1150.  *
  1151.  *        This function is called as a separate process. It is used
  1152.  *        to service requests for pattern matching by patched code.
  1153.  *        We do this in a separate process to ensure we have enough
  1154.  *        stack to perform the pattern matching (pattern matching can
  1155.  *        be very stack-intensive.)
  1156.  *
  1157.  *        This process is also used to do pathname expansion when we
  1158.  *        are monitoring packets -- this is needed for both the
  1159.  *        ShowAllPaths option and monitoring of the ACTION_MAKE_LINK packet.
  1160.  *
  1161.  *        We keep going until we get a QUIT_MSG from the main task,
  1162.  *        at which point we exit.
  1163.  */
  1164. void __saveds BackgroundProcCode(void)
  1165. {
  1166. #if DEBUG_PATTERN
  1167.     BPTR dbfile = Open("CON:100/100/400/100/Pattern/WAIT/AUTO/CLOSE",
  1168.                        MODE_OLDFILE);
  1169. #define DBP(s)    FPrintf s
  1170. #else
  1171. #define DBP(s)
  1172. #endif
  1173.  
  1174.     Task  *quittask;
  1175.     ULONG  quitsig;
  1176.     int    done = 0;
  1177.  
  1178.     /*
  1179.      *        Our first task is to re-route the message port allocated
  1180.      *        on our behalf by the main process so that it points to us.
  1181.      *        We also re-enable signals on incoming messages to ensure
  1182.      *        we are correctly interrupted.
  1183.      */        
  1184.     BackgroundPort.mp_SigTask = SysBase->ThisTask;
  1185.     BackgroundPort.mp_SigBit  = AllocSignal(-1);    /* Better not fail!        */
  1186.     BackgroundPort.mp_Flags   = PA_SIGNAL;            /* Allow signalling now    */
  1187.     DBP((dbfile, "Started pattern debug code\n"));
  1188.  
  1189.     while (!done) {
  1190.         /*        Now handle all pattern messages queue on the pattern port.
  1191.          *        Unlike normal messages, we don't simply reply to these
  1192.          *        with ReplyMsg(); instead, we signal the indicated task
  1193.          *        using the signal bit specified in the message.
  1194.          *
  1195.          *        This is because the sender may not have an available
  1196.          *        signal bit to use to receive the reply so it uses a
  1197.          *        system bit which is guaranteed to be unused.
  1198.          *
  1199.          *        Note that we grab the pattern semapore while we process
  1200.          *        the messages; this stops the mainline code from updating
  1201.          *        the middle of the buffer while we are doing a comparison.
  1202.          */
  1203.         PatternMsg      *pmsg;
  1204.         NameFromLockMsg *lmsg;
  1205.  
  1206.         WaitPort(&BackgroundPort);
  1207.  
  1208.         ObtainSemaphore(&PatternBufSem);
  1209.         while ((pmsg = (void *)GetMsg(&BackgroundPort)) != NULL) {
  1210.             DBP((dbfile, "Got a message, type = %ld!\n", pmsg->type));
  1211.             switch (pmsg->type) {
  1212.  
  1213.                 case QUIT_MSG:
  1214.                     /*
  1215.                      *        We've been told to quit by the main task, so
  1216.                      *        set some variables appropriately. We flush
  1217.                      *        the remaining messages in the queue first
  1218.                      *        just to be safe (we wouldn't want to leave
  1219.                      *        anyone hanging, now, would we?)
  1220.                      */
  1221.                     PatternEnabled    = 0;    /* Safety first */
  1222.                     done             = 1;
  1223.                     quittask        = pmsg->task;
  1224.                     quitsig          = pmsg->sigmask;
  1225.                     break;
  1226.  
  1227.                 case PATTERN_MSG:
  1228.                     DBP((dbfile, "Got a pmatch from %s\n", pmsg->name));
  1229.                     if (PatternEnabled)
  1230.                         pmsg->match = MatchPatternNoCase(PatternBuf,
  1231.                                                          pmsg->name);
  1232.                     else
  1233.                         pmsg->match = 1; /* If disabled, everything matches */
  1234.  
  1235.                     Signal(pmsg->task, pmsg->sigmask);
  1236.                     break;
  1237.  
  1238.                 case PATHEXPAND_MSG:
  1239.                     lmsg         = (NameFromLockMsg *)pmsg;
  1240.                     lmsg->newbuf = MyNameFromLock(lmsg->lock, lmsg->filename,
  1241.                                                   lmsg->buf,  lmsg->maxlen);
  1242.                     Signal(lmsg->task, lmsg->sigmask);
  1243.                     break;
  1244.             }
  1245.         }
  1246.         ReleaseSemaphore(&PatternBufSem);
  1247.     }
  1248.  
  1249. #if DEBUG_PATTERN
  1250.     DBP((dbfile, "Quitting...\n"));
  1251.     Close(dbfile);
  1252. #endif
  1253.  
  1254.     /*
  1255.      *        Now cleanup and exit from this task. We need to Forbid()
  1256.      *        before we signal the main task that we're ready to quit,
  1257.      *        since otherwise the main task might go ahead and unload
  1258.      *        us before we have a chance to execute the last few lines
  1259.      *        in the function (e.g. if the main task is running at a
  1260.      *        higher priority than we are).
  1261.      */        
  1262.     Forbid();
  1263.     FreeSignal(BackgroundPort.mp_SigBit);
  1264.     Signal(quittask, quitsig);
  1265. }
  1266.  
  1267. /*
  1268.  *        CheckPattern(name, namelen)
  1269.  *
  1270.  *        Checks if our task's name has been masked out by the
  1271.  *        user, using our current AmigaDOS pattern string.
  1272.  *
  1273.  *        We can't call the DOS pattern match function directly, due to
  1274.  *        stack limitations (the caller may not have a very big stack).
  1275.  *        So, we maintain a cache of recently used tasks -- the first
  1276.  *        task on the list is always the most recently monitored task.
  1277.  *
  1278.  *        If a task doesn't appear on the list, or if it appears on the
  1279.  *        list but its name has changed, then we send a message to the
  1280.  *        main SnoopDos task asking it to check the pattern. We also
  1281.  *        add the result of this check to our cache for future calls.
  1282.  *
  1283.  *        Sending a message to another task from within a patch is tricky;
  1284.  *        more specifically, receiving the acknowledgement back from the
  1285.  *        main task is tricky. This is because we don't know what signal
  1286.  *        bits the caller might be using, and so we can't just grab one.
  1287.  *        We could use AllocSignal(), but then what if the caller has no
  1288.  *        free signal bits?
  1289.  *
  1290.  *        Our solution (not necessarily the best) is to use the reserved
  1291.  *        exec signal bit SIGB_SINGLE. This is used for semaphore operations,
  1292.  *        among others, and is only ever used while waiting for a single
  1293.  *        event to happen. Thus, when our code is reached, we know this
  1294.  *        bit is not in use and thus we can use it ourselves.
  1295.  *
  1296.  *        Returns true if no pattern is set, or if the pattern matches
  1297.  *        the current task name. Returns false otherwise.
  1298.  *
  1299.  */
  1300. int CheckPattern(char *taskname, int namelen)
  1301. {
  1302.     PatternCache *pc;
  1303.     Task *thistask = SysBase->ThisTask;
  1304.     PatternMsg pmsg;
  1305.  
  1306.     if (!PatternEnabled)
  1307.         return (TRUE);
  1308.  
  1309.     if (!PatternWildcard)
  1310.         return (stricmp(taskname, PatternString) == 0);
  1311.  
  1312.     namelen = MIN(namelen, PC_NAMELEN-1);
  1313.  
  1314.     /*
  1315.      *        Got a pattern, so search our cache list
  1316.      */
  1317.     ObtainSemaphore(&PatternCacheSem);
  1318.     FORLIST(&PatternList, pc)
  1319.         if (pc->task == thistask)
  1320.             break;
  1321.     
  1322.     /*
  1323.      *        Okay, now pc is either the matching task, or else a pointer
  1324.      *        to the end of our list.
  1325.      */
  1326.     if (NextNode(pc) && pc->task == thistask) {
  1327.         /*
  1328.          *        We broke out of the loop since we matched the task.
  1329.          *        Now check if the name matches too.
  1330.          */
  1331.         if (strncmp(pc->name, taskname, namelen) == 0) {
  1332.             /*
  1333.              *        We're the same. Move this node to the start of
  1334.              *        the list, and return the appropriate state.
  1335.              */
  1336.             Remove((Node *)pc);
  1337.             AddHead(&PatternList, (Node *)pc);
  1338.             ReleaseSemaphore(&PatternCacheSem);
  1339.             return (pc->match);
  1340.         }
  1341.         /*
  1342.          *        The name is different but the Task ID is the same --
  1343.          *        most likely, someone ran a new CLI command. Thus, use
  1344.          *        this cache entry for our new task name and fall through.
  1345.          */
  1346.     } else {
  1347.         /*
  1348.          *        We didn't match a task so grab one from the end of the
  1349.          *        list and use that instead.
  1350.          */
  1351.         pc         = TailNode(&PatternList);
  1352.         pc->task = thistask;
  1353.     }
  1354.  
  1355.     /*
  1356.      *        Move the node to the start of the list to stop it being flushed
  1357.      *        any time soon, and update the name to match our own name.
  1358.      */
  1359.     Remove((Node *)pc);
  1360.     AddHead(&PatternList, (Node *)pc);
  1361.     strncpy(pc->name, taskname, namelen);
  1362.     pc->name[namelen] = 0;
  1363.     ReleaseSemaphore(&PatternCacheSem);
  1364.  
  1365.     /*
  1366.      *        Now send a message to the main task asking it to perform a name
  1367.      *        match for us. We need to be careful to make sure our pmsg won't
  1368.      *        be confused with a DOS packet by our PutMsg() patch -- we do
  1369.      *        this by NULLing out the name field.
  1370.      */
  1371.     pmsg.type                = PATTERN_MSG;
  1372.     pmsg.name                 = taskname;
  1373.     pmsg.sigmask             = SIGF_SINGLE;
  1374.     pmsg.task                 = thistask;
  1375.     pmsg.msg.mn_Node.ln_Name = NULL;
  1376.  
  1377.     SetSignal(0, SIGF_SINGLE);        /* Ensure signal is clear first        */
  1378.     PutMsg(&BackgroundPort, &pmsg);    /* Send request to background task    */
  1379.     Wait(SIGF_SINGLE);                /* Wait for acknowledgement            */
  1380.     pc->match = pmsg.match;            /* And save result in cache            */
  1381.  
  1382.     return (pmsg.match);
  1383. }
  1384.  
  1385. /*
  1386.  *        GetVolName(lock, buf, maxlen)
  1387.  *
  1388.  *        Copies the volume name associated with lock into the buffer,
  1389.  *        with terminating ':'. If lock is NULL, the volume address is
  1390.  *        taken directly from volume.
  1391.  *
  1392.  *        If UseDevNames is true, the device list is searched looking
  1393.  *        for the device node associated with the volume node (i.e. two
  1394.  *        nodes sharing the same task address).
  1395.  *
  1396.  *        WARNING: This function must not be called from within a DOS
  1397.  *                 device handler due to potential deadlock errors!
  1398.  *
  1399.  */
  1400. void GetVolName(BPTR lock, char *buf, int maxlen)
  1401. {
  1402.     struct DeviceList *vol;
  1403.     struct DosList *dl;
  1404.     int gotdev = 0;
  1405.  
  1406.     if (lock == NULL) {
  1407.         NameFromLock(lock, buf, maxlen);
  1408.         return;
  1409.     }
  1410.     vol = BTOC(((struct FileLock *)BTOC(lock))->fl_Volume);
  1411.  
  1412.     if (UseDevNames == 0 || vol->dl_Task == NULL) {
  1413.         /*
  1414.          *        Use volume name, especially if the volume isn't currently
  1415.          *        mounted!
  1416.          */
  1417.         UBYTE *volname = BTOC(vol->dl_Name); 
  1418.         int len = MIN(maxlen-2, *volname);
  1419.  
  1420.         memcpy(buf, volname+1, len);
  1421.         buf[len++] = ':';
  1422.         buf[len] = '\0';
  1423.         return;
  1424.     }
  1425.  
  1426.     /*
  1427.      *        The user wants the device name. The only way to obtain this
  1428.      *        is to search the device list looking for the device node with
  1429.      *        the same task address as this volume.
  1430.      */
  1431.     dl = LockDosList(LDF_DEVICES | LDF_READ);
  1432.     while (dl = NextDosEntry(dl, LDF_DEVICES)) {
  1433.         if (dl->dol_Task == vol->dl_Task) {
  1434.             /*
  1435.              *        Found our task, so now copy device name
  1436.              */
  1437.             UBYTE *devname = BTOC(dl->dol_Name);
  1438.             int len = MIN(maxlen-2, *devname);
  1439.  
  1440.             memcpy(buf, devname+1, len);
  1441.             buf[len++] = ':';
  1442.             buf[len] = '\0';
  1443.             gotdev = 1;
  1444.             break;
  1445.         }
  1446.     }
  1447.     UnLockDosList(LDF_DEVICES | LDF_READ);
  1448.     if (!gotdev)
  1449.         strcpy(buf, "???:");
  1450. }
  1451.  
  1452. /*
  1453.  *        MyNameFromLock(lock, filename, buf, maxlen)
  1454.  *
  1455.  *        This is a custom version of the DOS function NameFromLock()
  1456.  *        which expands a disk lock into a full path.
  1457.  *
  1458.  *        Our version adds the following features. The device name will be
  1459.  *        given as either the physical device (like DH0:) if UseDevNames
  1460.  *        is true, or the volume name (like System3.0:) if UseDevNames is
  1461.  *        false.
  1462.  *
  1463.  *        If filename is non-NULL, then it will be appended to the lock
  1464.  *        path. If filename contains path info, then this will be taken
  1465.  *        into account when generating the lock, so that an absolute path
  1466.  *        in filename will not have any effect, and a relative filename
  1467.  *        (like //directory) will cause the original lock to be ParentDir()'d
  1468.  *        twice before being resolved.
  1469.  *
  1470.  *        This function can't fail. In the event of an error (string too
  1471.  *        long, or something like that), the buffer will be filled accordingly.
  1472.  *        It is assumed that the buffer will always be big enough to hold short
  1473.  *        error messages or a volume name.
  1474.  *
  1475.  *        Returns a pointer to the path (which will not necessarily be at
  1476.  *        the start of the buffer, but is guaranteed null-terminated.)
  1477.  *        Note that it's even possible that the pointer returned will be
  1478.  *        to the original filename if no path expansion was required.
  1479.  *
  1480.  *        New: We now preserve the IoErr() that was present on entry, since
  1481.  *        it may have been set by the calling function if OnlyShowFails is
  1482.  *        true. Otherwise, IoErr() will be screwed up by the operations we
  1483.  *        do here (e.g. SAS/C deleting a non-existent file when OnlyShowFails
  1484.  *        is true).
  1485.  *
  1486.  *        WARNING: This function must not be called from within a DOS
  1487.  *                 device handler due to potential deadlock errors!
  1488.  */        
  1489. char *MyNameFromLock(BPTR lock, char *filename, char *buf, int maxlen)
  1490. {
  1491.     Process *myproc = (Process *)SysBase->ThisTask;
  1492.     int pos = maxlen - 1;
  1493.     D_S(fib, struct FileInfoBlock);
  1494.     LONG savedioerr = IoErr();
  1495.     BPTR curlock;
  1496.     BPTR newlock;
  1497.     void *savewinptr;
  1498.     char *p;
  1499.     int len;
  1500.     int skipfirstslash = 0;    /* If true, skip first slash when building name */
  1501.     int err = 0;
  1502.  
  1503.     /*
  1504.      *        Check for special magic filehandle
  1505.      */
  1506.     if (filename && *filename) {
  1507.         if (strcmp(filename, "*") == 0)
  1508.             return (filename);
  1509.  
  1510.         /*
  1511.          *        First determine if we have any work to do.
  1512.          */
  1513.         if (*filename == ':') {
  1514.             /*
  1515.              *        Got a reference relative to the root directory. Simply
  1516.              *        grab the volume (or device) name from the lock and go
  1517.              *        with that.
  1518.              */
  1519.             int len;
  1520.  
  1521.             GetVolName(lock, buf, maxlen);
  1522.             len = strlen(buf);
  1523.             strncat(buf+len, filename+1, maxlen-len);
  1524.             buf[maxlen-1] = '\0';
  1525.             SetIoErr(savedioerr);
  1526.             return (buf);
  1527.         }
  1528.         for (p = filename; *p; p++) {
  1529.             if (*p == ':')            /* If absolute path name, leave it alone */
  1530.                 return (filename);
  1531.         }
  1532.     } else {
  1533.         /*
  1534.          *        Filename is null, so indicate we want to skip the first
  1535.          *        slash when building the directory path
  1536.          */
  1537.         skipfirstslash = 1;
  1538.     }
  1539.  
  1540.     savewinptr = myproc->pr_WindowPtr;
  1541.     myproc->pr_WindowPtr = (APTR)-1;        /* Disable error requesters      */
  1542.  
  1543.     newlock = DupLock(lock);
  1544.     if (lock && !newlock) {
  1545.         GetVolName(lock, buf, 20);
  1546.         if (filename) {
  1547.             strcat(buf, ".../");
  1548.             strcat(buf, filename);
  1549.         }
  1550.         myproc->pr_WindowPtr = savewinptr;    /* Re-enable error requesters */
  1551.         SetIoErr(savedioerr);
  1552.         return (buf);
  1553.     }
  1554.     buf[pos] = '\0';
  1555.     curlock = newlock;
  1556.     if (filename) {
  1557.         while (newlock && *filename == '/') {
  1558.             /*
  1559.              *        Handle leading /'s by moving back a directory level
  1560.              *        but nothing else
  1561.              */
  1562.             newlock = ParentDir(curlock);
  1563.             if (newlock) {
  1564.                 UnLock(curlock);
  1565.                 curlock = newlock;
  1566.                 filename++;
  1567.             }
  1568.         }
  1569.         len = strlen(filename);
  1570.         if (len > (pos-2)) {
  1571.             memcpy(buf+2, filename+len-pos, pos-2);
  1572.             buf[0] = buf[1] = '.';
  1573.             pos = 0;
  1574.             UnLock(curlock);
  1575.         } else {
  1576.             pos -= len;
  1577.             memcpy(buf+pos, filename, len);
  1578.         }
  1579.     }
  1580.  
  1581.     /*
  1582.      *        At this point, we have buf containing the filename (minus any
  1583.      *        leading /'s), starting at the index given by pos. If filename
  1584.      *        was NULL or empty, then pos indexes to a \0 terminator.
  1585.      *
  1586.      *        Next, we want to pre-pend directory names to the front of
  1587.      *        the filename (assuming there _is_ a filename) until we get
  1588.      *        to the device root.
  1589.      */
  1590.     newlock = curlock;
  1591.     while (newlock) {
  1592.  
  1593.         if (!Examine(curlock, fib)) {
  1594.             err++;
  1595.             break;
  1596.         }
  1597.         len = strlen(fib->fib_FileName);
  1598.         if (len > (pos-3)) {
  1599.             /*
  1600.              *        Not enough room: prefix dots at start to indicate
  1601.              *        an overrun. We use pos-3 since we need one char
  1602.              *        for a possible slash and two more to accomodate a
  1603.              *        leading ".."
  1604.              */
  1605.             memcpy(buf+2, fib->fib_FileName+len-pos+3, pos-2);
  1606.             buf[0] = buf[1] = '.';
  1607.             buf[pos-1] = '/';
  1608.             pos = 0;
  1609.             break;
  1610.         }
  1611.         newlock = ParentDir(curlock);
  1612.         if (newlock) {
  1613.             UnLock(curlock);
  1614.             curlock = newlock;
  1615.             pos -= len + 1;
  1616.             memcpy(buf + pos, fib->fib_FileName, len);
  1617.             if (skipfirstslash) {
  1618.                 skipfirstslash = 0;
  1619.                 buf[pos+len] = '\0';
  1620.             } else
  1621.                 buf[pos+len] = '/';
  1622.         }
  1623.     }
  1624.     /*
  1625.      *        Now we've built the path components; add the volume node
  1626.      *        to the beginning if possible.
  1627.      */
  1628.     if (err) {
  1629.         /*
  1630.          *        If an error occurred, the volume is probably not mounted,
  1631.          *        so we include a ".../" component in the path to show
  1632.          *        we couldn't get all the info
  1633.          */
  1634.         pos -= 4;
  1635.         memcpy(buf + pos, ".../", 4);
  1636.     }
  1637.     if (pos > 0) {
  1638.         char volname[20];
  1639.         int len;
  1640.         char *p;
  1641.  
  1642.         GetVolName(curlock, volname, 20);
  1643.         len = strlen(volname);
  1644.         if (len > pos) {
  1645.             p = volname + len - pos;
  1646.             len = pos;
  1647.         } else {
  1648.             p = volname;
  1649.         }
  1650.         pos -= len;
  1651.         memcpy(buf + pos, p, len);
  1652.     }
  1653.     if (curlock)
  1654.         UnLock(curlock);
  1655.  
  1656.     myproc->pr_WindowPtr = savewinptr;        /* Re-enable error requesters */
  1657.     SetIoErr(savedioerr);
  1658.     return (buf+pos);
  1659. }
  1660.  
  1661. /*
  1662.  *        AsyncNameFromLock(volname, lock, filename, dest, maxlen)
  1663.  *
  1664.  *        This is identical to the MyNameFromLock() function except that
  1665.  *        it gets the background SnoopDos process to do the path expansion
  1666.  *        instead of doinng it from within the caller's process. This is
  1667.  *        necessary if we need to expand paths and we are not sure if the
  1668.  *        caller's process mesage port is free.
  1669.  *
  1670.  *        Volname is the volume on which the lock resides. If the lock itself
  1671.  *        is NULL, then the root of the volume will be assumed and this
  1672.  *        string will be copied into the buffer as the volume name.
  1673.  *
  1674.  *        WARNING: This function must not be called from within a DOS
  1675.  *                 device handler due to potential deadlock errors!
  1676.  */
  1677. char *AsyncNameFromLock(char *volname, BPTR lock, char *filename,
  1678.                         char *buf, int maxlen)
  1679. {
  1680.     NameFromLockMsg lmsg;
  1681.  
  1682.     if (lock == NULL) {
  1683.         /*
  1684.          *        Do a quick scan on the filename itself to see
  1685.          *        if it contains a colon prefixed by some text;
  1686.          *        if it does, then we already have a full path.
  1687.          *        If it's just a colon, prefix it with the
  1688.          *        volume name. If it has no colon, prefix it
  1689.          *        with the volume name and a colon.
  1690.          */
  1691.         char *p;
  1692.  
  1693.         strcpy(buf, volname);
  1694.         if (filename) {
  1695.             if (*filename == ':') {
  1696.                 strcat(buf, filename);
  1697.             } else {
  1698.                 for (p = filename; *p && *p != ':'; p++)
  1699.                     ;
  1700.                 if (*p)
  1701.                     strcpy(buf, filename);
  1702.                 else
  1703.                     mysprintf(buf, "%s:%s", volname, filename);
  1704.             }
  1705.         }
  1706.         return (buf);
  1707.     }
  1708.     lmsg.type        = PATHEXPAND_MSG;
  1709.     lmsg.lock        = lock;
  1710.     lmsg.filename    = filename;
  1711.     lmsg.buf        = buf;
  1712.     lmsg.maxlen        = maxlen;
  1713.     lmsg.sigmask    = SIGF_SINGLE;
  1714.     lmsg.task        = SysBase->ThisTask;
  1715.     lmsg.msg.mn_Node.ln_Name = NULL;
  1716.  
  1717.     SetSignal(0, SIGF_SINGLE);        /* Ensure signal is clear first        */
  1718.     PutMsg(&BackgroundPort, &lmsg);    /* Send request to background task    */
  1719.     Wait(SIGF_SINGLE);                /* Wait for acknowledgement            */
  1720.     return (lmsg.newbuf);
  1721. }
  1722.  
  1723. #if STACK_CHECK
  1724. /*
  1725.  *        GetFreeStack()
  1726.  *
  1727.  *        Returns the amount of free space available on the calling task's
  1728.  *        stack (thanks to Ralph Babel for this).
  1729.  */
  1730. ULONG GetFreeStack(void)
  1731. {
  1732.     Process *pr = (Process *)SysBase->ThisTask;
  1733.     char *upper;
  1734.     char *lower;
  1735.     ULONG total;
  1736.     ULONG avail;
  1737.  
  1738.     if (pr->pr_Task.tc_Node.ln_Type == NT_PROCESS && pr->pr_CLI != NULL) {
  1739.         upper = (char *)pr->pr_ReturnAddr + sizeof(ULONG);
  1740.         total = *(ULONG *)pr->pr_ReturnAddr;
  1741.         lower = upper - total;
  1742.     } else {
  1743.         upper = (char *)pr->pr_Task.tc_SPUpper;
  1744.         lower = (char *)pr->pr_Task.tc_SPLower;
  1745.         total = upper - lower;
  1746.     }
  1747.     avail = (char *)getreg(REG_A7) - lower;
  1748.     return (avail);
  1749. }
  1750. #endif
  1751.  
  1752. /*
  1753.  *        CreateEvent(calladdr, seqnum, action, filename,
  1754.  *                    options, expandname [, lock] )
  1755.  *
  1756.  *        Allocates a new event for the current task and initialises
  1757.  *        almost all the fields in it accordingly.
  1758.  *
  1759.  *        seqnum is initialised to the sequence number of the new event. This
  1760.  *        is used to allow later detection of events which have scrolled off
  1761.  *        the top of the buffer.
  1762.  *
  1763.  *        filename and options are text strings, and actionid is a message ID.
  1764.  *        The storage for these will be allocated in the event buffer.
  1765.  *
  1766.  *        If expandname is EXPAND_NAME, then 'filename' is assumed to represent
  1767.  *        a real disk filename, and it will be expanded to a full path if that
  1768.  *        option is set. If expandname is NO_EXPAND, then the name is passed
  1769.  *        unchanged.
  1770.  *
  1771.  *        If expandname is neither EXPAND_NAME or NO_EXPAND, then it is
  1772.  *        interpreted as a volume name, and one additional parameter
  1773.  *        following it will be expected -- this additional parameter will
  1774.  *        be a lock on that volume. The filename will then be expanded
  1775.  *        to a full pathname on that volume. If the lock is null, then it
  1776.  *        is assumed to represent the root of the volume. The filename
  1777.  *        expansion is done by the background process, making it safe to
  1778.  *        call from within the PutMsg() patch which monitors raw packets.
  1779.  *        
  1780.  *        If you pass in a NULL value for any of the three pointers, it will
  1781.  *        be made to point to a blank string.
  1782.  *
  1783.  *        If for some reason the event could not be created, returns NULL,
  1784.  *        else returns a pointer to the initialised event.
  1785.  *
  1786.  *        Possible reasons for failure include:
  1787.  *
  1788.  *            - Calling task is main SnoopDos task (!)
  1789.  *            - Calling task is not included in the match pattern
  1790.  *            - Calling task was called by a ROM function
  1791.  *            - Disk error occurred expanding filename
  1792.  *            - Out of memory allocating new event
  1793.  */
  1794. struct Event *CreateEvent(ULONG calladdr, LONG *seqnum, ULONG actionid,
  1795.                           char *filename, char *options, int expandname, ...)
  1796. {
  1797.     Task *thistask = SysBase->ThisTask;
  1798.     struct CommandLineInterface *cli;
  1799.     Event *ev;
  1800.     char *procname;
  1801.     char *strptr;
  1802.     char *action;
  1803.     char namebuf[MAX_SHORT_LEN+1];
  1804.     char pathbuf[MAX_STR_LEN+1];
  1805.     int plen = 0;
  1806.     int flen = 0;
  1807.     int olen = 0;
  1808.     int alen = 0;
  1809.     int slen = 0;
  1810.     int clinum;
  1811.  
  1812.     /*
  1813.      *        We ignore all calls from the main SnoopDOS process and
  1814.      *        background process, and also any nested calls made while
  1815.      *        we have the semaphore locked (which might otherwise lead
  1816.      *        to nasty trashing of the list!)
  1817.      *
  1818.      *        Also, any calls from input.device are ignored since these
  1819.      *        typically call us with layers locked, and the slightest
  1820.      *        delay could be fatal.
  1821.      */
  1822.     if (thistask == SnoopTask || thistask == (Task *)BackgroundProc
  1823.                               || thistask == BufSem.ss_Owner
  1824.                               || thistask == RamLibTask
  1825.                               || thistask == InputTask)
  1826.         return (NULL);
  1827.  
  1828.     /*
  1829.      *        We also ignore any calls made from ROM addresses (if this
  1830.      *        option is enabled), since this cuts down the amount of
  1831.      *        superfluos output considerably.
  1832.      *
  1833.      *        We specifically DO allow calls from within ramlib, since
  1834.      *        it's very easy to disable ramlib directly if need be (by
  1835.      *        putting ~ramlib in the match name) and ramlib does
  1836.      *        many useful operations that are convenient to monitor.
  1837.      */
  1838.     if (thistask != RealRamLibTask && !MonROMCalls &&
  1839.                               calladdr >= RomStart && calladdr <= RomEnd)
  1840.         return (NULL);
  1841.  
  1842.     /*
  1843.      *        Work out the correct name for our task. This will either be
  1844.      *        the task name, the process name, or the current CLI command
  1845.      *        (if the process is a CLI, _and_ is currently running a command.)
  1846.      *
  1847.      *        In addition, for CLIs, we set clinum to the CLI number. For
  1848.      *        tasks and non-CLI processes, clinum becomes zero.
  1849.      */
  1850.     procname = thistask->tc_Node.ln_Name;
  1851.     plen     = strlen(procname);
  1852.     cli         = BTOC(((Process *)thistask)->pr_CLI);
  1853.     clinum   = 0;
  1854.  
  1855.     if (thistask->tc_Node.ln_Type == NT_PROCESS && cli) {
  1856.         /*
  1857.          *        If we have a CLI command, we have to convert the name
  1858.          *        from a BSTR into a C string
  1859.          */
  1860.         clinum = ((Process *)thistask)->pr_TaskNum;
  1861.         if (cli->cli_Module) {
  1862.             UBYTE *cliname = BTOC(cli->cli_CommandName);
  1863.             plen = *cliname;
  1864.  
  1865.             if (plen) {
  1866.                 if (plen > MAX_SHORT_LEN)
  1867.                     plen = MAX_SHORT_LEN;
  1868.                 memcpy(namebuf, cliname+1, plen);
  1869.                 namebuf[plen] = 0;
  1870.             } else {
  1871.                 strcpy(namebuf, "<Unknown>");
  1872.                 plen = strlen(namebuf);
  1873.             }
  1874.             procname = namebuf;
  1875.         }
  1876.     }
  1877.  
  1878. #if 0
  1879.     if (stricmp(procname, "ramlib") == 0) {
  1880.         KPrintF("Ramlib: Stacksize is %ld\n", GetFreeStack());
  1881.     }
  1882. #endif
  1883.  
  1884.     if (!CheckPattern(procname, plen))        /* Perform pattern check */
  1885.         return (FALSE);
  1886.  
  1887.     /*
  1888.      *        Now deal with our three strings
  1889.      */
  1890.     if (ShowPaths && filename != NULL) {
  1891.         if (expandname == EXPAND_NAME) {
  1892.             /*
  1893.              *        Expand filename to full path
  1894.              */
  1895.             filename = MyNameFromLock(((Process *)thistask)->pr_CurrentDir,
  1896.                                       filename, pathbuf, MAX_STR_LEN);
  1897.         } else if (expandname != NO_EXPAND) {
  1898.             /*
  1899.              *        Expand filename to full path using background process
  1900.              *        and the lock specified in expandname.
  1901.              */
  1902.             BPTR lock = *(ULONG *)(&expandname+1);
  1903.  
  1904.             filename = AsyncNameFromLock((char *)expandname, lock, filename, 
  1905.                                          pathbuf, MAX_STR_LEN);
  1906.         }
  1907.     }
  1908.     plen++;
  1909.     if (ShowCLINum && clinum)
  1910.         plen += 8;                        /* Leave room for CLI display */
  1911.  
  1912.     action = MSG(actionid);
  1913.     alen   = strlen(action) + 1;
  1914.  
  1915.     if (filename)    flen = strlen(filename)    + 1;
  1916.     if (options)    olen = strlen(options)    + 1;
  1917.  
  1918.     if (SegTrackerActive)
  1919.         slen = MAX_SEGTRACKER_LEN;
  1920.  
  1921.     /*
  1922.      *        If the calling task has locked the layers of the SnoopDos
  1923.      *        screen, then we ignore all calls by that task. This prevents
  1924.      *        us deadlocking when we wait for the semaphore (while meanwhile,
  1925.      *        the SnoopDos mainline code could own the semaphore, and be
  1926.      *        waiting for the layers to be unlocked by our caller so it can
  1927.      *        render some output).
  1928.      *
  1929.      *        We Forbid() to make sure the window doesn't get closed from
  1930.      *        under our feet while we're checking it. We have to try and
  1931.      *        allocate the event while still Forbid'd since otherwise, there
  1932.      *        is a small window where the main window could open, lock the
  1933.      *        layers, and lock the semaphore, in between our check. It's
  1934.      *        unlikely, but not _that_ unlikely (especially since the main
  1935.      *        window always does an immediate redraw when it opens)
  1936.      */        
  1937.     Forbid();
  1938.     if (MainWindow &&
  1939.             (MainWindow->RPort->Layer->Lock.ss_Owner   == thistask ||
  1940.              MainWindow->WScreen->LayerInfo.Lock.ss_Owner == thistask)) {
  1941.         Permit();
  1942.         return (NULL);
  1943.     }
  1944.     ev = GetNewEvent(plen + alen + flen + olen + slen);
  1945.     Permit();
  1946.  
  1947.     if (!ev)
  1948.         return (NULL);
  1949.     
  1950.     ev->action = ev->filename = ev->options = ev->result = "";
  1951.  
  1952.     strptr = (char *)(ev+1);
  1953.  
  1954.     if (ShowCLINum && clinum)
  1955.         mysprintf(strptr, "[%ld] %s", clinum, procname);
  1956.     else
  1957.         strcpy(strptr, procname);
  1958.     ev->procname  = strptr;
  1959.     strptr       += plen;
  1960.  
  1961. //    KPrintF("New event: %ld, %s, %s\n", ev->seqnum, procname, action); /* DB */
  1962.     if (alen) {
  1963.         strcpy(strptr, action);
  1964.         ev->action   = strptr;
  1965.         strptr          += alen;
  1966.     }
  1967.     if (flen) {
  1968.         strcpy(strptr, filename);
  1969.         ev->filename  = strptr;
  1970.         strptr        += flen;
  1971.     }
  1972.     if (olen) {
  1973.         strcpy(strptr, options);
  1974.         ev->options   = strptr;
  1975.         strptr       += olen;
  1976.     }
  1977.     if (slen) {
  1978.         ev->segname      = strptr;
  1979.         strptr       += slen;
  1980.     } else {
  1981.         ev->segname   = NULL;    /* Say no segtracker storage allocated */
  1982.     }
  1983.     // DateStamp(&ev->datestamp);
  1984.     ev->calladdr    = calladdr;
  1985.     ev->processid    = (ULONG)thistask;
  1986.     // ev->processid    = GetFreeStack();
  1987.  
  1988.     if (seqnum)
  1989.         *seqnum = ev->seqnum;
  1990.     return (ev);
  1991. }
  1992.  
  1993. /*
  1994.  *        CheckTaskRecursion()
  1995.  *
  1996.  *        Checks if the current task is already listed as being on the
  1997.  *        recursion list. If so, returns FALSE, indicating that the
  1998.  *        caller should immediately pass control back to the original
  1999.  *        function and not try and do anything else.
  2000.  *
  2001.  *        Otherwise, records the task's address in a recursion list
  2002.  *        and returns a handle. This handle should be passed back to
  2003.  *        EndTaskRecursion() when the function is complete, which will
  2004.  *        cause it to be removed from the list.
  2005.  *
  2006.  *        OpenLibrary() needs to surround its call to CreateEvent() with
  2007.  *        CheckTaskRecursion()/EndTaskRecursion. Also, GetMsg()/PutMsg() need 
  2008.  *        this to check if any of the DOS packets it monitors have been
  2009.  *        generated by AmigaDOS functions we monitor.
  2010.  */
  2011. ULONG CheckTaskRecursion(void)
  2012. {
  2013.     Task *taskid = SysBase->ThisTask;
  2014.     ULONG taskhandle;
  2015.     int i;
  2016.  
  2017.     for (i = 1; i < NUM_CACHED_TASKS; i++)
  2018.         if (CachedTask[i] == taskid)
  2019.             return (0);
  2020.     
  2021.     ObtainSemaphore(&TaskCacheSem);
  2022.     taskhandle = TaskCacheIndex++;
  2023.     if (TaskCacheIndex >= NUM_CACHED_TASKS)
  2024.         TaskCacheIndex = 1;
  2025.     CachedTask[taskhandle] = taskid;
  2026.     ReleaseSemaphore(&TaskCacheSem);
  2027.  
  2028.     return (taskhandle);
  2029. }
  2030.  
  2031. /*
  2032.  *        EndTaskRecursion(taskhandle)
  2033.  *
  2034.  *        Called to remove a task from the recursion list.
  2035.  *        See CheckTaskRecursion() above.
  2036.  */
  2037. void EndTaskRecursion(ULONG taskhandle)
  2038. {
  2039.     CachedTask[taskhandle] = 0;
  2040. }
  2041.  
  2042. #if 0
  2043. /*
  2044.  *        TestPatch()
  2045.  *
  2046.  *        Patches a single function to try it out
  2047.  */
  2048. void TestPatch(void)
  2049. {
  2050.     extern List            EventList;            /* List of events        */
  2051.     static EventFormat evformat[50];
  2052.     static char outstr[500];
  2053.     char *formatstr = "%d %t %c %n %s %h %a %40f %o %r";
  2054.     int formatlen;
  2055.  
  2056.     OnlyShowFails    = 0;
  2057.     SegTrackerActive = 1;
  2058.     formatlen = ParseFormatString(formatstr, evformat, 50);
  2059.  
  2060.     if (!InitPatches()) {
  2061.         printf("Patch initialisation failed!\n");
  2062.         return;
  2063.     }
  2064.  
  2065.     PatchList[GID_OPENFILE].enabled = 1;
  2066.     UpdatePatches();
  2067.     printf("Patch now enabled!\n\n");
  2068.  
  2069.     FormatEvent(evformat, NULL, outstr, 0, formatlen-1);
  2070.     printf("%s\n", outstr);
  2071.     printf("%s\n", UnderlineTitles(evformat, outstr, '-'));
  2072.  
  2073.     for (;;) {
  2074.         ULONG mask;
  2075.  
  2076.         mask = Wait(SIGBREAKF_CTRL_C | NewEventMask);
  2077.         if (mask & SIGBREAKF_CTRL_C)
  2078.             break;
  2079.  
  2080.         if (mask & NewEventMask) {
  2081.             Event *ev;
  2082.  
  2083.             SetSignal(0, NewEventMask);    /* Clear signal for next time */
  2084.             FORLIST(&EventList, ev) {
  2085.                 int stat = ev->status;
  2086.  
  2087.                 if (stat == ES_UPDATING || stat == ES_READY) {
  2088.                     FormatEvent(evformat, ev, outstr, 0, formatlen-1);
  2089.                     printf("%s\n", outstr);
  2090.                     if (stat == ES_READY)
  2091.                         ev->status = ES_ACCEPTED;
  2092.                 }
  2093.             }
  2094.         }
  2095.     }
  2096.     PatchList[GID_OPENFILE].enabled = 0;
  2097.     UpdatePatches();
  2098.     printf("Patch disabled\n");
  2099.     CleanupPatches();
  2100. }
  2101. #endif
  2102.  
  2103. /*
  2104.  *        HandlePaused(event, seqnum)
  2105.  *
  2106.  *        Should be called when pausing is enabled -- it sets the
  2107.  *        event Result code to "...." to indicate the event is about
  2108.  *        to be executed, then waits for the pausing to be turned off.
  2109.  *        If you pass in NULL as the event code, then this isn't done.
  2110.  *
  2111.  *        seqnum is the actual event number remembered by the caller,
  2112.  *        not (necessarily) the event number stored in the event. This
  2113.  *        is so that the code can check if the event has rolled off the
  2114.  *        top of the buffer or not before modifying it.
  2115.  *
  2116.  *        There are some circumstances where it is dangerous for
  2117.  *        us to pause because we are being called by a system task
  2118.  *        that is needed by the main SnoopDos program.
  2119.  *
  2120.  *        We work around this as follows. Firstly, if the caller happens
  2121.  *        to own the semaphore associated with the layer info for the
  2122.  *        window or screen containing the SnoopDos window, then we
  2123.  *        don't try and pause it since otherwise we might deadlock
  2124.  *        (the user wouldn't be able to unpause SnoopDos because that
  2125.  *        requires clicking on a button.)
  2126.  *
  2127.  *        Secondly, whenever SnoopDos itself does something that might
  2128.  *        stop it processing the main messages (e.g. show a requester,
  2129.  *        open a file, check for ASL, etc.) then pausing is temporarily
  2130.  *        turned off since we have no way of knowing whether a third
  2131.  *        party patch might be spinning off a background task to do it
  2132.  *        and the background task might try and wait on this. Bummer, eh?
  2133.  */
  2134. void HandlePaused(Event *ev, LONG seqnum)
  2135. {
  2136.     if (Paused) {
  2137.         /*
  2138.          *        Need to forbid when checking layers since otherwise the
  2139.          *        main window might close while we're reading its pointers.
  2140.          */
  2141.         int gotlock = 0;
  2142.         char *oldresmsg;
  2143.  
  2144.         Forbid();
  2145.         if (MainWindow) {
  2146.             struct Layer *layer;
  2147.             struct Layer_Info *li;
  2148.             Task *thistask = SysBase->ThisTask;
  2149.  
  2150.             layer = MainWindow->RPort->Layer;
  2151.             li    = &MainWindow->WScreen->LayerInfo;
  2152.  
  2153.             gotlock = (li->Lock.ss_Owner    == thistask) ||
  2154.                       (layer->Lock.ss_Owner == thistask);
  2155.         }
  2156.         Permit();
  2157.         if (gotlock)
  2158.             return;
  2159.  
  2160.         /*
  2161.          *        Okay, safe to proceed with pausing. We simply set the
  2162.          *        result message to indicate we're paused, signal
  2163.          *        the main task, wait for the pausing to finish, restore
  2164.          *        the result message, and return.
  2165.          */
  2166.         if (ev) {
  2167.             ObtainSemaphore(&BufSem);
  2168.             if (seqnum >= RealFirstSeq) {
  2169.                 oldresmsg  = ev->result;
  2170.                 ev->result = MSG(MSG_RES_PAUSE);
  2171.             }
  2172.             ReleaseSemaphore(&BufSem);
  2173.         }
  2174.         Signal(SnoopTask, NewEventMask);    /* Make sure main task updates */
  2175.         ObtainSemaphore(&PauseSem);
  2176.         ReleaseSemaphore(&PauseSem);
  2177.         if (ev) {
  2178.             ObtainSemaphore(&BufSem);
  2179.             if (seqnum >= RealFirstSeq)
  2180.                 ev->result = oldresmsg;
  2181.             ReleaseSemaphore(&BufSem);
  2182.         }
  2183.     }
  2184. }
  2185.  
  2186. #define CheckForPause(ev,seqnum)    if (Paused)    { HandlePaused(ev, seqnum); }
  2187.  
  2188. /*****************************************************************************
  2189.  *
  2190.  *        Now here are all the patches for the 25 or so functions that we
  2191.  *        patch into. Most of the patches follow a similar template, and
  2192.  *        share similar lower-level functions, but are different enough to
  2193.  *        prevent us from using the same patch for several functions.
  2194.  *
  2195.  *        We always signal both before and after a particular call, so that
  2196.  *        in the event of a call causing a crash, it will still show up in
  2197.  *        the SnoopDos window.
  2198.  *
  2199.  *        The OnlyShowFails flag is used to say whether we want to monitor
  2200.  *        all calls to a function, or only those which return a failure code.
  2201.  *        If the latter, we need to call the function first before deciding
  2202.  *        whether or not to create an event to record it -- in this case, the
  2203.  *        result code remains cached for the remainder of the function. The
  2204.  *        rest of the time, we create the event and signal the main task,
  2205.  *        then call the function to get the result, and finally update the
  2206.  *        event with the result and signal the main task again.
  2207.  *
  2208.  *        Each patch that calls a function and then updates the buffer on its
  2209.  *        return must ensure that the buffer entry is has a pointer to is still
  2210.  *        valid before it updates it. It does this by remembering the sequence
  2211.  *        number of the buffer entry, and then checking that the sequence
  2212.  *        number of the first buffer entry is still <= that (i.e. the entry
  2213.  *        is still somewhere in the buffer). This is vital to ensure that
  2214.  *        innocent buffer memory isn't trashed by old events.
  2215.  *
  2216.  *****************************************************************************/
  2217.  
  2218. /*
  2219.  *        New_FindPort(name)
  2220.  *
  2221.  *        Searches the system port list looking for a port name
  2222.  */
  2223. ULONG ASM New_FindPort(reg_a1 char *name, reg_a6 void *libbase)
  2224. {
  2225.     MarkCallAddr;
  2226.     FP_FindPort origfunc = (FP_FindPort)PatchList[GID_FINDPORT].origfunc;
  2227.     int onlyfails = OnlyShowFails;
  2228.     ULONG result;
  2229.     Event *ev;
  2230.     LONG  seqnum;
  2231.  
  2232.     if (onlyfails) {
  2233.         result = origfunc(name, libbase);
  2234.         if (result)
  2235.             return (result);
  2236.     }
  2237.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDPORT,
  2238.                      name, NULL, NO_EXPAND);
  2239.     if (!ev) {
  2240.         if (onlyfails)
  2241.             return (result);
  2242.         JumpOrigFunc(0);
  2243.     }
  2244.     
  2245.     /*
  2246.      *        Allocated event safely, now tell SnoopDos task to wake up
  2247.      */
  2248.     if (onlyfails) {
  2249.         CheckForPause(NULL, 0);
  2250.     } else {
  2251.         ev->status = ES_UPDATING;
  2252.         CheckForPause(ev, seqnum);
  2253.         Signal(SnoopTask, NewEventMask);
  2254.         result = origfunc(name, libbase);
  2255.     }
  2256.     ObtainSemaphore(&BufSem);
  2257.     if (seqnum >= RealFirstSeq) {
  2258.         if (result)
  2259.             ev->result = MSG(MSG_RES_OKAY);
  2260.         else
  2261.             ev->result = MSG(MSG_RES_FAIL);
  2262.         ev->status = ES_READY;
  2263.         Signal(SnoopTask, NewEventMask);
  2264.     }
  2265.     ReleaseSemaphore(&BufSem);
  2266.     return (result);
  2267. }
  2268.  
  2269. /*
  2270.  *        New_FindResident(name)
  2271.  *
  2272.  *        Searches the system resident list looking for a resident module
  2273.  */
  2274. ULONG ASM New_FindResident(reg_a1 char *name, reg_a6 void *libbase)
  2275. {
  2276.     MarkCallAddr;
  2277.     FP_FindResident origfunc = (FP_FindResident)
  2278.                                 PatchList[GID_FINDRESIDENT].origfunc;
  2279.     int onlyfails = OnlyShowFails;
  2280.     ULONG result;
  2281.     Event *ev;
  2282.     LONG  seqnum;
  2283.  
  2284.     if (SysBase->ThisTask == RealRamLibTask)
  2285.         JumpOrigFunc(0);
  2286.  
  2287.     if (onlyfails) {
  2288.         result = origfunc(name, libbase);
  2289.         if (result)
  2290.             return (result);
  2291.     }
  2292.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDRESIDENT,
  2293.                      name, NULL, NO_EXPAND);
  2294.     if (!ev) {
  2295.         if (onlyfails)
  2296.             return (result);
  2297.         JumpOrigFunc(0);
  2298.     }
  2299.     
  2300.     /*
  2301.      *        Allocated event safely, now tell SnoopDos task to wake up
  2302.      */
  2303.     if (onlyfails) {
  2304.         CheckForPause(NULL, 0);
  2305.     } else {
  2306.         ev->status = ES_UPDATING;
  2307.         CheckForPause(ev, seqnum);
  2308.         Signal(SnoopTask, NewEventMask);
  2309.         result = origfunc(name, libbase);
  2310.     }
  2311.     ObtainSemaphore(&BufSem);
  2312.     if (seqnum >= RealFirstSeq) {
  2313.         if (result)
  2314.             ev->result = MSG(MSG_RES_OKAY);
  2315.         else
  2316.             ev->result = MSG(MSG_RES_FAIL);
  2317.         ev->status = ES_READY;
  2318.         Signal(SnoopTask, NewEventMask);
  2319.     }
  2320.     ReleaseSemaphore(&BufSem);
  2321.     return (result);
  2322. }
  2323.  
  2324. /*
  2325.  *        New_FindSemaphore(name)
  2326.  *
  2327.  *        Searches the system semaphore list looking for the named semaphore
  2328.  */
  2329. ULONG ASM New_FindSemaphore(reg_a1 char *name, reg_a6 void *libbase)
  2330. {
  2331.     MarkCallAddr;
  2332.     FP_FindSemaphore origfunc = (FP_FindSemaphore)
  2333.                                   PatchList[GID_FINDSEMAPHORE].origfunc;
  2334.     int onlyfails = OnlyShowFails;
  2335.     ULONG result;
  2336.     Event *ev;
  2337.     LONG  seqnum;
  2338.  
  2339.     if (onlyfails) {
  2340.         result = origfunc(name, libbase);
  2341.         if (result)
  2342.             return (result);
  2343.     }
  2344.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDSEM,
  2345.                      name, NULL, NO_EXPAND);
  2346.     if (!ev) {
  2347.         if (onlyfails)
  2348.             return (result);
  2349.         JumpOrigFunc(0);
  2350.     }
  2351.     
  2352.     /*
  2353.      *        Allocated event safely, now tell SnoopDos task to wake up
  2354.      */
  2355.     if (onlyfails) {
  2356.         CheckForPause(NULL, 0);
  2357.     } else {
  2358.         ev->status = ES_UPDATING;
  2359.         CheckForPause(ev, seqnum);
  2360.         Signal(SnoopTask, NewEventMask);
  2361.         result = origfunc(name, libbase);
  2362.     }
  2363.     ObtainSemaphore(&BufSem);
  2364.     if (seqnum >= RealFirstSeq) {
  2365.         if (result)
  2366.             ev->result = MSG(MSG_RES_OKAY);
  2367.         else
  2368.             ev->result = MSG(MSG_RES_FAIL);
  2369.         ev->status = ES_READY;
  2370.         Signal(SnoopTask, NewEventMask);
  2371.     }
  2372.     ReleaseSemaphore(&BufSem);
  2373.     return (result);
  2374. }
  2375.     
  2376. /*
  2377.  *        New_FindTask(name)
  2378.  *
  2379.  *        Searches the system task list looking for the named task.
  2380.  *        We ignore calls to FindTask(0) since this is just
  2381.  *        looking for a pointer to the current task.
  2382.  */
  2383. ULONG ASM New_FindTask(reg_a1 char *name, reg_a6 void *libbase)
  2384. {
  2385.     MarkCallAddr;
  2386.     FP_FindTask origfunc = (FP_FindTask)PatchList[GID_FINDTASK].origfunc;
  2387.     int onlyfails = OnlyShowFails;
  2388.     ULONG result;
  2389.     Event *ev;
  2390.     LONG  seqnum;
  2391.  
  2392.     if (name == NULL)
  2393.         return (origfunc(name, libbase));
  2394.  
  2395.     if (onlyfails) {
  2396.         result = origfunc(name, libbase);
  2397.         if (result)
  2398.             return (result);
  2399.     }
  2400.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDTASK,
  2401.                      name, NULL, NO_EXPAND);
  2402.     if (!ev) {
  2403.         if (onlyfails)
  2404.             return (result);
  2405.         JumpOrigFunc(0);
  2406.     }
  2407.     
  2408.     /*
  2409.      *        Allocated event safely, now tell SnoopDos task to wake up
  2410.      */
  2411.     if (onlyfails) {
  2412.         CheckForPause(NULL, 0);
  2413.     } else {
  2414.         ev->status = ES_UPDATING;
  2415.         CheckForPause(ev, seqnum);
  2416.         Signal(SnoopTask, NewEventMask);
  2417.         result = origfunc(name, libbase);
  2418.     }
  2419.     ObtainSemaphore(&BufSem);
  2420.     if (seqnum >= RealFirstSeq) {
  2421.         if (result)
  2422.             ev->result = MSG(MSG_RES_OKAY);
  2423.         else
  2424.             ev->result = MSG(MSG_RES_FAIL);
  2425.         ev->status = ES_READY;
  2426.         Signal(SnoopTask, NewEventMask);
  2427.     }
  2428.     ReleaseSemaphore(&BufSem);
  2429.     return (result);
  2430. }
  2431.  
  2432. /*
  2433.  *        New_LockPubScreen(name)
  2434.  *
  2435.  *        Obtains a lock on the named public screen. If the name is NULL,
  2436.  *        then the caller is looking for a Lock() on the default screen,
  2437.  *        and we ignore it (otherwise we'd get loads of output).
  2438.  */
  2439. ULONG ASM New_LockPubScreen(reg_a0 char *name, reg_a6 void *libbase)
  2440. {
  2441.     MarkCallAddr;
  2442.     FP_LockPubScreen origfunc = (FP_LockPubScreen)
  2443.                                 PatchList[GID_LOCKSCREEN].origfunc;
  2444.     int onlyfails = OnlyShowFails;
  2445.     ULONG result;
  2446.     Event *ev;
  2447.     LONG  seqnum;
  2448.  
  2449.     if (!name)
  2450.         JumpOrigFunc(0);
  2451.  
  2452.     if (onlyfails) {
  2453.         result = origfunc(name, libbase);
  2454.         if (result)
  2455.             return (result);
  2456.     }
  2457.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_LOCKSCREEN,
  2458.                      name, NULL, NO_EXPAND);
  2459.     if (!ev) {
  2460.         if (onlyfails)
  2461.             return (result);
  2462.         JumpOrigFunc(0);
  2463.     }
  2464.     
  2465.     /*
  2466.      *        Allocated event safely, now tell SnoopDos task to wake up
  2467.      */
  2468.     if (onlyfails) {
  2469.         CheckForPause(NULL, 0);
  2470.     } else {
  2471.         ev->status = ES_UPDATING;
  2472.         Signal(SnoopTask, NewEventMask);
  2473.         CheckForPause(ev, seqnum);
  2474.         result = origfunc(name, libbase);
  2475.     }
  2476.     ObtainSemaphore(&BufSem);
  2477.     if (seqnum >= RealFirstSeq) {
  2478.         if (result)
  2479.             ev->result = MSG(MSG_RES_OKAY);
  2480.         else
  2481.             ev->result = MSG(MSG_RES_FAIL);
  2482.         ev->status = ES_READY;
  2483.         Signal(SnoopTask, NewEventMask);
  2484.     }
  2485.     ReleaseSemaphore(&BufSem);
  2486.     return (result);
  2487. }
  2488.  
  2489. /*
  2490.  *        New_OpenDevice(name, unit, ioreq, flags)
  2491.  *
  2492.  *        Opens a new device. We need to be careful with the error return
  2493.  *        for this function, since success is indicated by a return of 0!
  2494.  *
  2495.  *        N.b. Kickstart 3.1 has a space-saving hack which allows a NULL
  2496.  *        device name to be interpreted as a magic cookie indicating a
  2497.  *        particular ROM devices. We ignore such calls (especially from CON,
  2498.  *        which does it a lot!) but flag other occurrances of NULL device
  2499.  *        names as an error.
  2500.  */
  2501. ULONG ASM New_OpenDevice(reg_a0 char *name, reg_d0 long unit,
  2502.                          reg_a1 struct IORequest *ioreq,
  2503.                          reg_d1 long flags, reg_a6 void *libbase)
  2504. {
  2505.     MarkCallAddr;
  2506.     FP_OpenDevice origfunc = (FP_OpenDevice)PatchList[GID_OPENDEVICE].origfunc;
  2507.     int onlyfails = OnlyShowFails;
  2508.     ULONG result;
  2509.     Event *ev;
  2510.     char unitstr[20];
  2511.     char *devname = name;
  2512.     LONG  seqnum;
  2513.  
  2514. /*
  2515.  *        Release Semaphore replacement code for debugging patch
  2516.  */
  2517. #if MONITOR_SEMAPHORE
  2518.     Semaphore *sem = (Semaphore *)name;
  2519.  
  2520.     if (LoadTask == SysBase->ThisTask)
  2521.         KPrintF("ReleaseSemaphore in LoadSeg: retaddr = %08lx\n", CallAddr);
  2522.     return origfunc(name, unit, ioreq, flags, libbase);
  2523. #endif
  2524.  
  2525.     if (onlyfails) {
  2526.         result = origfunc(name, unit, ioreq, flags, libbase);
  2527.         if (!result)
  2528.             return (result);
  2529.     }
  2530.     /*
  2531.      *        Now a workaround for a bug in CON that makes it call
  2532.      *        OpenDevice("timer.device" | NULL) very frequently.
  2533.      *        (The NULL is a space-saving shorthand which the 3.1 Kickstart
  2534.      *        interprets as a magic cookie for "timer.device" to save some
  2535.      *        ROM space).
  2536.      */
  2537.     if ((CallAddr >= RomStart && CallAddr <= RomEnd) ||
  2538.                     strcmp(SysBase->ThisTask->tc_Node.ln_Name, "CON") == 0) {
  2539.         if (onlyfails)
  2540.             return (result);
  2541.         JumpOrigFunc(unit);
  2542.     }
  2543.     if (!devname)
  2544.         devname = MSG(MSG_NULLSTR);
  2545.  
  2546.     mysprintf(unitstr, MSG(MSG_OPT_DEVUNIT), unit);
  2547.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENDEV,
  2548.                      devname, unitstr, NO_EXPAND);
  2549.     if (!ev) {
  2550.         if (onlyfails)
  2551.             return (result);
  2552.         JumpOrigFunc(unit);
  2553.     }
  2554.     
  2555.     /*
  2556.      *        Allocated event safely, now tell SnoopDos task to wake up
  2557.      */
  2558.     if (onlyfails) {
  2559.         CheckForPause(NULL, 0);
  2560.     } else {
  2561.         ev->status = ES_UPDATING;
  2562.         CheckForPause(ev, seqnum);
  2563.         Signal(SnoopTask, NewEventMask);
  2564.         result = origfunc(name, unit, ioreq, flags, libbase);
  2565.     }
  2566.     ObtainSemaphore(&BufSem);
  2567.     if (seqnum >= RealFirstSeq) {
  2568.         if (!result)
  2569.             ev->result = MSG(MSG_RES_OKAY);
  2570.         else
  2571.             ev->result = MSG(MSG_RES_FAIL);
  2572.         ev->status = ES_READY;
  2573.         Signal(SnoopTask, NewEventMask);
  2574.     }
  2575.     ReleaseSemaphore(&BufSem);
  2576.     return (result);
  2577. }
  2578.  
  2579. /*
  2580.  *        New_OpenFont(textattr)
  2581.  *
  2582.  *        Opens a new font that's currently in memory. OpenDiskFont will
  2583.  *        call this function first before checking the disk, so patching
  2584.  *        this function catches all accesses.
  2585.  */
  2586. ULONG ASM New_OpenFont(reg_a0 struct TextAttr *textattr, reg_a6 void *libbase)
  2587. {
  2588.     MarkCallAddr;
  2589.     FP_OpenFont origfunc = (FP_OpenFont)PatchList[GID_OPENFONT].origfunc;
  2590.     int onlyfails = OnlyShowFails;
  2591.     ULONG result;
  2592.     Event *ev;
  2593.     char *name;
  2594.     char sizestr[20];
  2595.     LONG  seqnum;
  2596.  
  2597.     if (onlyfails) {
  2598.         result = origfunc(textattr, libbase);
  2599.         if (result)
  2600.             return (result);
  2601.     }
  2602.     if (textattr) {
  2603.         mysprintf(sizestr, MSG(MSG_OPT_FONTSIZE), textattr->ta_YSize);
  2604.         name = textattr->ta_Name;
  2605.     } else {
  2606.         *sizestr = '\0';
  2607.         name = MSG(MSG_NULLSTR);
  2608.     }
  2609.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENFONT,
  2610.                      name, sizestr, NO_EXPAND);
  2611.     if (!ev) {
  2612.         if (onlyfails)
  2613.             return (result);
  2614.         JumpOrigFunc(0);
  2615.     }
  2616.     
  2617.     /*
  2618.      *        Allocated event safely, now tell SnoopDos task to wake up
  2619.      */
  2620.     if (onlyfails) {
  2621.         CheckForPause(NULL, 0);
  2622.     } else {
  2623.         ev->status = ES_UPDATING;
  2624.         CheckForPause(ev, seqnum);
  2625.         Signal(SnoopTask, NewEventMask);
  2626.         result = origfunc(textattr, libbase);
  2627.     }
  2628.     ObtainSemaphore(&BufSem);
  2629.     if (seqnum >= RealFirstSeq) {
  2630.         if (result)
  2631.             ev->result = MSG(MSG_RES_OKAY);
  2632.         else
  2633.             ev->result = MSG(MSG_RES_FAIL);
  2634.         ev->status = ES_READY;
  2635.         Signal(SnoopTask, NewEventMask);
  2636.     }
  2637.     ReleaseSemaphore(&BufSem);
  2638.     return (result);
  2639. }
  2640.  
  2641. /*
  2642.  *        New_OpenLibrary(name, version)
  2643.  *
  2644.  *        Opens the named library with the specified version number.
  2645.  *
  2646.  *        We have a nasty potential for deadlock here -- CreateEvent()
  2647.  *        which we call, in turns calls DateStamp(), and DateStamp()
  2648.  *        itself calls OpenLibrary(....). So, we need to protect
  2649.  *        ourselves against a recursive call.
  2650.  *
  2651.  *        We do this by ignoring any call to open dos.library made
  2652.  *        by a call originating in ROM.
  2653.  */
  2654. ULONG ASM New_OpenLibrary(reg_a1 char *name, reg_d0 long version,
  2655.                           reg_a6 void *libbase)
  2656. {
  2657.     MarkCallAddr;
  2658.     FP_OpenLibrary origfunc = (FP_OpenLibrary)
  2659.                                PatchList[GID_OPENLIBRARY].origfunc;
  2660.     int onlyfails = OnlyShowFails;
  2661.     ULONG result;
  2662.     Event *ev;
  2663.     char verstr[20];
  2664.     ULONG taskhandle;
  2665.     LONG  seqnum;
  2666.  
  2667.     /*
  2668.      *        Check for re-entrant calling (usually caused by the
  2669.      *        DateStamp call in the GetNewEvent() code).
  2670.      *
  2671.      *        In theory, we should only need to check if the semaphore
  2672.      *        is owned by the current task. In practise, we need
  2673.      *        the additional checks to be really sure (otherwise
  2674.      *        we seem to get unexplained crashes very frequently --
  2675.      *        very mysterious and worrying).
  2676.      *
  2677.      *        We also ignore calls to open dos.library -- this is partly
  2678.      *        because it gets called by DateStamp() inside dos.library
  2679.      *        itself (so things that call DateStamp once a second, like
  2680.      *        Enforcer(), will generate loads of calls to it) and partly
  2681.      *        to avoid problems with our own CreateEvent() code calling it.
  2682.      */
  2683.     if (BufSem.ss_Owner == SysBase->ThisTask ||
  2684.         strcmp(name, "dos.library") == 0)
  2685.     {
  2686.         JumpOrigFunc(version);
  2687.     }
  2688.  
  2689.     if (onlyfails) {
  2690.         result = origfunc(name, version, libbase);
  2691.         if (result)
  2692.             return (result);
  2693.     }
  2694.     taskhandle = CheckTaskRecursion();
  2695.     if (!taskhandle) {
  2696.         if (!onlyfails)
  2697.             result = origfunc(name, version, libbase);
  2698.         return (result);
  2699.     }
  2700.     mysprintf(verstr, MSG(MSG_OPT_LIBVER), version);
  2701.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENLIB,
  2702.                      name, verstr, NO_EXPAND);
  2703.     EndTaskRecursion(taskhandle);
  2704.  
  2705.     if (!ev) {
  2706.         if (onlyfails)
  2707.             return (result);
  2708.         JumpOrigFunc(version);
  2709.     }
  2710.     
  2711.     /*
  2712.      *        Allocated event safely, now tell SnoopDos task to wake up
  2713.      */
  2714.     if (onlyfails) {
  2715.         CheckForPause(NULL, 0);
  2716.     } else {
  2717.         ev->status = ES_UPDATING;
  2718.         CheckForPause(ev, seqnum);
  2719.         Signal(SnoopTask, NewEventMask);
  2720.         result = origfunc(name, version, libbase);
  2721.     }
  2722.     ObtainSemaphore(&BufSem);
  2723.     if (seqnum >= RealFirstSeq) {
  2724.         if (result)
  2725.             ev->result = MSG(MSG_RES_OKAY);
  2726.         else
  2727.             ev->result = MSG(MSG_RES_FAIL);
  2728.         ev->status = ES_READY;
  2729.         Signal(SnoopTask, NewEventMask);
  2730.     }
  2731.     ReleaseSemaphore(&BufSem);
  2732.     return (result);
  2733. }
  2734.  
  2735. /*
  2736.  *        New_OpenResource(name)
  2737.  *
  2738.  *        Opens the named resource
  2739.  */
  2740. ULONG ASM New_OpenResource(reg_a1 char *name, reg_a6 void *libbase)
  2741. {
  2742.     MarkCallAddr;
  2743.     FP_OpenResource origfunc = (FP_OpenResource)
  2744.                                 PatchList[GID_OPENRESOURCE].origfunc;
  2745.     int onlyfails = OnlyShowFails;
  2746.     ULONG result;
  2747.     Event *ev;
  2748.     LONG  seqnum;
  2749.  
  2750.     if (onlyfails) {
  2751.         result = origfunc(name, libbase);
  2752.         if (result)
  2753.             return (result);
  2754.     }
  2755.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPENRES,
  2756.                      name, NULL, NO_EXPAND);
  2757.     if (!ev) {
  2758.         if (onlyfails)
  2759.             return (result);
  2760.         JumpOrigFunc(0);
  2761.     }
  2762.     
  2763.     /*
  2764.      *        Allocated event safely, now tell SnoopDos task to wake up
  2765.      */
  2766.     if (onlyfails) {
  2767.         CheckForPause(NULL, 0);
  2768.     } else {
  2769.         ev->status = ES_UPDATING;
  2770.         CheckForPause(ev, seqnum);
  2771.         Signal(SnoopTask, NewEventMask);
  2772.         result = origfunc(name, libbase);
  2773.     }
  2774.     ObtainSemaphore(&BufSem);
  2775.     if (seqnum >= RealFirstSeq) {
  2776.         if (result)
  2777.             ev->result = MSG(MSG_RES_OKAY);
  2778.         else
  2779.             ev->result = MSG(MSG_RES_FAIL);
  2780.         ev->status = ES_READY;
  2781.         Signal(SnoopTask, NewEventMask);
  2782.     }
  2783.     ReleaseSemaphore(&BufSem);
  2784.     return (result);
  2785. }
  2786.  
  2787. /*
  2788.  *        New_FindToolType(array, tooltype)
  2789.  *
  2790.  *        Searches the tooltype array for a particular tooltype
  2791.  */
  2792. ULONG ASM New_FindToolType(reg_a0 char **array, reg_a1 char *tooltype,
  2793.                            reg_a6 void *libbase)
  2794. {
  2795.     MarkCallAddr;
  2796.     FP_FindToolType origfunc = (FP_FindToolType)
  2797.                                 PatchList[GID_READTOOLTYPES].origfunc;
  2798.     int onlyfails = OnlyShowFails;
  2799.     ULONG result;
  2800.     Event *ev;
  2801.     char *toolname = tooltype;
  2802.     LONG  seqnum;
  2803.  
  2804.     if (onlyfails) {
  2805.         result = origfunc(array, tooltype, libbase);
  2806.         if (result)
  2807.             return (result);
  2808.     }
  2809.     if (toolname == NULL)
  2810.         toolname = MSG(MSG_NULLSTR);
  2811.  
  2812.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDTOOL,
  2813.                      toolname, NULL, NO_EXPAND);
  2814.     if (!ev) {
  2815.         if (onlyfails)
  2816.             return (result);
  2817.         JumpOrigFunc(0);
  2818.     }
  2819.     
  2820.     /*
  2821.      *        Allocated event safely, now tell SnoopDos task to wake up
  2822.      */
  2823.     if (onlyfails) {
  2824.         CheckForPause(NULL, 0);
  2825.     } else {
  2826.         ev->status = ES_UPDATING;
  2827.         CheckForPause(ev, seqnum);
  2828.         Signal(SnoopTask, NewEventMask);
  2829.         result = origfunc(array, tooltype, libbase);
  2830.     }
  2831.     ObtainSemaphore(&BufSem);
  2832.     if (seqnum >= RealFirstSeq) {
  2833.         if (result)
  2834.             ev->result = MSG(MSG_RES_OKAY);
  2835.         else
  2836.             ev->result = MSG(MSG_RES_FAIL);
  2837.         ev->status = ES_READY;
  2838.         Signal(SnoopTask, NewEventMask);
  2839.     }
  2840.     ReleaseSemaphore(&BufSem);
  2841.     return (result);
  2842. }
  2843.  
  2844. /*
  2845.  *        New_MatchToolValue(tooltype, value)
  2846.  *
  2847.  *        Checks if a specified tooltype contains a given value
  2848.  */
  2849. ULONG ASM New_MatchToolValue(reg_a0 char *tooltype, reg_a1 char *value,
  2850.                              reg_a6 void *libbase)
  2851. {
  2852.     MarkCallAddr;
  2853.     FP_MatchToolValue origfunc = (FP_MatchToolValue)
  2854.                                   PatchList[GID_READTOOLTYPES2].origfunc;
  2855.     int onlyfails = OnlyShowFails;
  2856.     ULONG result;
  2857.     Event *ev;
  2858.     char *toolname = tooltype;
  2859.     LONG  seqnum;
  2860.  
  2861.     if (onlyfails) {
  2862.         result = origfunc(tooltype, value, libbase);
  2863.         if (result)
  2864.             return (result);
  2865.     }
  2866.     if (toolname == NULL)
  2867.         toolname = MSG(MSG_NULLSTR);
  2868.  
  2869.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_MATCHTOOL,
  2870.                      toolname, value, NO_EXPAND);
  2871.     if (!ev) {
  2872.         if (onlyfails)
  2873.             return (result);
  2874.         JumpOrigFunc(0);
  2875.     }
  2876.     
  2877.     /*
  2878.      *        Allocated event safely, now tell SnoopDos task to wake up
  2879.      */
  2880.     if (onlyfails) {
  2881.         CheckForPause(NULL, 0);
  2882.     } else {
  2883.         ev->status = ES_UPDATING;
  2884.         CheckForPause(ev, seqnum);
  2885.         Signal(SnoopTask, NewEventMask);
  2886.         result = origfunc(tooltype, value, libbase);
  2887.     }
  2888.     ObtainSemaphore(&BufSem);
  2889.     if (seqnum >= RealFirstSeq) {
  2890.         if (result)
  2891.             ev->result = MSG(MSG_RES_OKAY);
  2892.         else
  2893.             ev->result = MSG(MSG_RES_FAIL);
  2894.         ev->status = ES_READY;
  2895.         Signal(SnoopTask, NewEventMask);
  2896.     }
  2897.     ReleaseSemaphore(&BufSem);
  2898.     return (result);
  2899. }
  2900.  
  2901. /*
  2902.  *        New_CurrentDir()
  2903.  *
  2904.  *        Changes current directory to somewhere else
  2905.  */
  2906. ULONG ASM New_CurrentDir(reg_d1 BPTR lock, reg_a6 void *libbase)
  2907. {
  2908.     MarkCallAddr;
  2909.     FP_CurrentDir origfunc = (FP_CurrentDir)PatchList[GID_CHANGEDIR].origfunc;
  2910.     int onlyfails = OnlyShowFails;
  2911.     Event *ev;
  2912.     char lockbuf[MAX_LOCK_LEN+1];
  2913.     char *lockpath;
  2914.     LONG  seqnum;
  2915.  
  2916.     if (onlyfails)
  2917.         return origfunc(lock, libbase);        /* CurrentDir never fails */
  2918.  
  2919.     lockpath = MyNameFromLock(lock, NULL, lockbuf, MAX_LOCK_LEN);
  2920.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_CHANGEDIR,
  2921.                      lockpath, NULL, NO_EXPAND);
  2922.     if (!ev)
  2923.         JumpOrigFunc(0);
  2924.     
  2925.     /*
  2926.      *        Allocated event safely, now tell SnoopDos task to wake up
  2927.      */
  2928.     ev->status = ES_UPDATING;
  2929.     CheckForPause(ev, seqnum);
  2930.     ObtainSemaphore(&BufSem);
  2931.     if (seqnum >= RealFirstSeq) {
  2932.         ev->status = ES_READY;
  2933.         Signal(SnoopTask, NewEventMask);
  2934.     }
  2935.     ReleaseSemaphore(&BufSem);
  2936.     return origfunc(lock, libbase);
  2937. }
  2938.  
  2939. /*
  2940.  *        New_DeleteFile()
  2941.  *
  2942.  *        Deletes a file from disk
  2943.  */
  2944. ULONG ASM New_DeleteFile(reg_d1 char *name, reg_a6 void *libbase)
  2945. {
  2946.     MarkCallAddr;
  2947.     FP_DeleteFile origfunc = (FP_DeleteFile)PatchList[GID_DELETE].origfunc;
  2948.     int onlyfails = OnlyShowFails;
  2949.     Event *ev;
  2950.     ULONG result;
  2951.     LONG  seqnum;
  2952.  
  2953.     if (onlyfails) {
  2954.         result = origfunc(name, libbase);
  2955.         if (result)
  2956.             return (result);
  2957.     }
  2958.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_DELETE,
  2959.                      name, NULL, EXPAND_NAME);
  2960.     if (!ev) {
  2961.         if (onlyfails)
  2962.             return (result);
  2963.         JumpOrigFunc(0);
  2964.     }
  2965.     
  2966.     /*
  2967.      *        Allocated event safely, now tell SnoopDos task to wake up
  2968.      */
  2969.     if (onlyfails) {
  2970.         CheckForPause(NULL, 0);
  2971.     } else {
  2972.         ev->status = ES_UPDATING;
  2973.         CheckForPause(ev, seqnum);
  2974.         Signal(SnoopTask, NewEventMask);
  2975.         result = origfunc(name, libbase);
  2976.     }
  2977.     ObtainSemaphore(&BufSem);
  2978.     if (seqnum >= RealFirstSeq) {
  2979.         if (result)
  2980.             ev->result = MSG(MSG_RES_OKAY);
  2981.         else
  2982.             ev->result = MSG(MSG_RES_FAIL);
  2983.         ev->status = ES_READY;
  2984.         Signal(SnoopTask, NewEventMask);
  2985.     }
  2986.     ReleaseSemaphore(&BufSem);
  2987.     return (result);
  2988. }
  2989.  
  2990. /*
  2991.  *        New_Execute()
  2992.  *
  2993.  *        Executes a command from disk. Now superceded by System and RunCommand
  2994.  */
  2995. ULONG ASM New_Execute(reg_d1 char *cmdline, reg_d2 BPTR fin, reg_d3 BPTR fout,
  2996.                       reg_a6 void *libbase)
  2997. {
  2998.     MarkCallAddr;
  2999.     FP_Execute origfunc = (FP_Execute)PatchList[GID_EXECUTE].origfunc;
  3000.     int onlyfails = OnlyShowFails;
  3001.     Event *ev;
  3002.     ULONG result;
  3003.     char *optstr;
  3004.     LONG  seqnum;
  3005.  
  3006.     /*
  3007.      *        For functions that may potentially take a long time to complete,
  3008.      *        we check if they're being called from ROM straight away, so
  3009.      *        that we can exit and not hang around waiting for them if they
  3010.      *        are, even in the OnlyShowFails case.
  3011.      */
  3012.     if (!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd)
  3013.         JumpOrigFunc(0);
  3014.  
  3015.     if (onlyfails) {
  3016.         result = origfunc(cmdline, fin, fout, libbase);
  3017.         if (result)
  3018.             return (result);
  3019.     }
  3020.     if (fin == NULL)
  3021.         optstr = MSG(MSG_OPT_EXECSINGLE);
  3022.     else
  3023.         optstr = MSG(MSG_OPT_EXECBATCH);
  3024.  
  3025.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_EXECUTE,
  3026.                      cmdline, optstr, NO_EXPAND);
  3027.     if (!ev) {
  3028.         if (onlyfails)
  3029.             return (result);
  3030.         JumpOrigFunc(0);
  3031.     }
  3032.     
  3033.     /*
  3034.      *        Allocated event safely, now tell SnoopDos task to wake up
  3035.      */
  3036.     if (onlyfails) {
  3037.         CheckForPause(NULL, 0);
  3038.     } else {
  3039.         ev->status = ES_UPDATING;
  3040.         CheckForPause(ev, seqnum);
  3041.         Signal(SnoopTask, NewEventMask);
  3042.         result = origfunc(cmdline, fin, fout, libbase);
  3043.     }
  3044.     ObtainSemaphore(&BufSem);
  3045.     if (seqnum >= RealFirstSeq) {
  3046.         if (result)
  3047.             ev->result = MSG(MSG_RES_OKAY);
  3048.         else
  3049.             ev->result = MSG(MSG_RES_FAIL);
  3050.         ev->status = ES_READY;
  3051.         Signal(SnoopTask, NewEventMask);
  3052.     }
  3053.     ReleaseSemaphore(&BufSem);
  3054.     return (result);
  3055. }
  3056.  
  3057. /*
  3058.  *        New_GetVar()
  3059.  *
  3060.  *        Inquires about the value of an environment variable
  3061.  */
  3062. ULONG ASM New_GetVar(reg_d1 char *name, reg_d2 char *buffer,
  3063.                      reg_d3 size, reg_d4 flags, reg_a6 void *libbase)
  3064. {
  3065.     MarkCallAddr;
  3066.     FP_GetVar origfunc = (FP_GetVar)PatchList[GID_GETVAR].origfunc;
  3067.     int onlyfails = OnlyShowFails;
  3068.     Event *ev;
  3069.     ULONG result;
  3070.     char optstr[20];
  3071.     ULONG optid;
  3072.     LONG  seqnum;
  3073.  
  3074.     if (onlyfails) {
  3075.         result = origfunc(name, buffer, size, flags, libbase);
  3076.         if (result != -1)
  3077.             return (result);
  3078.     }
  3079.     /*
  3080.      *        Now determine our flags. We recognise global, local
  3081.      *        or either types of variable. Binary variables are
  3082.      *        flagged with a trailing asterisk.
  3083.      */
  3084.     if        (flags & GVF_GLOBAL_ONLY)    optid = MSG_OPT_GLOBAL;
  3085.     else if ((flags & 7) == LV_ALIAS)    optid = MSG_OPT_ALIAS;
  3086.     else if (flags & GVF_LOCAL_ONLY)    optid = MSG_OPT_LOCAL;
  3087.     else                                optid = MSG_OPT_ANY;
  3088.     strcpy(optstr, MSG(optid));
  3089.  
  3090.     if (flags & GVF_BINARY_VAR)            strcat(optstr, "*");
  3091.  
  3092.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_GETVAR,
  3093.                      name, optstr, NO_EXPAND);
  3094.     if (!ev) {
  3095.         if (onlyfails)
  3096.             return (result);
  3097.         JumpOrigFunc(0);
  3098.     }
  3099.     
  3100.     /*
  3101.      *        Allocated event safely, now tell SnoopDos task to wake up
  3102.      */
  3103.     if (onlyfails) {
  3104.         CheckForPause(NULL, 0);
  3105.     } else {
  3106.         ev->status = ES_UPDATING;
  3107.         CheckForPause(ev, seqnum);
  3108.         Signal(SnoopTask, NewEventMask);
  3109.         result = origfunc(name, buffer, size, flags, libbase);
  3110.     }
  3111.     ObtainSemaphore(&BufSem);
  3112.     if (seqnum >= RealFirstSeq) {
  3113.         if (result != -1)
  3114.             ev->result = MSG(MSG_RES_OKAY);
  3115.         else
  3116.             ev->result = MSG(MSG_RES_FAIL);
  3117.         ev->status = ES_READY;
  3118.         Signal(SnoopTask, NewEventMask);
  3119.     }
  3120.     ReleaseSemaphore(&BufSem);
  3121.     return (result);
  3122. }
  3123.  
  3124. /*
  3125.  *        New_FindVar()
  3126.  *
  3127.  *        Inquires about the value of a local environment variable
  3128.  */
  3129. ULONG ASM New_FindVar(reg_d1 char *name, reg_d2 ULONG type,
  3130.                       reg_a6 void *libbase)
  3131. {
  3132.     MarkCallAddr;
  3133.     FP_FindVar origfunc = (FP_FindVar)PatchList[GID_GETVAR2].origfunc;
  3134.     int onlyfails = OnlyShowFails;
  3135.     Event *ev;
  3136.     ULONG result;
  3137.     ULONG optid;
  3138.     LONG  seqnum;
  3139.  
  3140.     if (onlyfails) {
  3141.         result = origfunc(name, type, libbase);
  3142.         if (result)
  3143.             return (result);
  3144.     }
  3145.  
  3146.     if        ((type & 7) == LV_VAR)        optid = MSG_OPT_LOCAL;
  3147.     else if ((type & 7) == LV_ALIAS)    optid = MSG_OPT_ALIAS;
  3148.     else                                optid = MSG_OPT_UNKNOWN;
  3149.  
  3150.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_FINDVAR,
  3151.                      name, MSG(optid), NO_EXPAND);
  3152.     if (!ev) {
  3153.         if (onlyfails)
  3154.             return (result);
  3155.         JumpOrigFunc(0);
  3156.     }
  3157.     
  3158.     /*
  3159.      *        Allocated event safely, now tell SnoopDos task to wake up
  3160.      */
  3161.     if (onlyfails) {
  3162.         CheckForPause(NULL, 0);
  3163.     } else {
  3164.         ev->status = ES_UPDATING;
  3165.         Signal(SnoopTask, NewEventMask);
  3166.         CheckForPause(ev, seqnum);
  3167.         result = origfunc(name, type, libbase);
  3168.     }
  3169.     ObtainSemaphore(&BufSem);
  3170.     if (seqnum >= RealFirstSeq) {
  3171.         if (result)
  3172.             ev->result = MSG(MSG_RES_OKAY);
  3173.         else
  3174.             ev->result = MSG(MSG_RES_FAIL);
  3175.         ev->status = ES_READY;
  3176.         Signal(SnoopTask, NewEventMask);
  3177.     }
  3178.     ReleaseSemaphore(&BufSem);
  3179.     return (result);
  3180. }
  3181.  
  3182. /*
  3183.  *        New_SetVar()
  3184.  *
  3185.  *        Sets a (possibly new) variable to a particular value
  3186.  */
  3187. ULONG ASM New_SetVar(reg_d1 char *name, reg_d2 char *buffer,
  3188.                      reg_d3 size, reg_d4 flags, reg_a6 void *libbase)
  3189. {
  3190.     MarkCallAddr;
  3191.     FP_SetVar origfunc = (FP_SetVar)PatchList[GID_SETVAR].origfunc;
  3192.     int onlyfails = OnlyShowFails;
  3193.     Event *ev;
  3194.     ULONG result;
  3195.     char varstr[MAX_STR_LEN+1];
  3196.     int vlen;
  3197.     ULONG optid;
  3198.     LONG  seqnum;
  3199.  
  3200.     if (onlyfails) {
  3201.         result = origfunc(name, buffer, size, flags, libbase);
  3202.         if (result)
  3203.             return (result);
  3204.     }
  3205.     /*
  3206.      *        Now determine our flags. We recognise global, local
  3207.      *        or alias variables.
  3208.      */
  3209.     if        (flags & GVF_GLOBAL_ONLY)    optid = MSG_OPT_GLOBAL;
  3210.     else if ((flags & 7) == LV_VAR)        optid = MSG_OPT_LOCAL;
  3211.     else if ((flags & 7) == LV_ALIAS)    optid = MSG_OPT_ALIAS;
  3212.     else                                optid = MSG_OPT_UNKNOWN;
  3213.  
  3214.     /*
  3215.      *        Now create a string that looks like "Variable=Value"
  3216.      *
  3217.      *        We go to some pains to ensure we don't overwrite our
  3218.      *        string length
  3219.      */
  3220.     vlen = strlen(name);
  3221.     if (vlen > (MAX_STR_LEN-1)) {
  3222.         strncpy(varstr, name, MAX_STR_LEN);
  3223.         varstr[MAX_STR_LEN] = 0;
  3224.     } else {
  3225.         strcpy(varstr, name);
  3226.         strcat(varstr, "=");
  3227.         vlen = 98 - vlen;
  3228.         if (size != -1)
  3229.             vlen = MIN(vlen, size);
  3230.  
  3231.         strncat(varstr, buffer, vlen);
  3232.         varstr[MAX_STR_LEN] = 0;
  3233.     }
  3234.  
  3235.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_SETVAR,
  3236.                      varstr, MSG(optid), NO_EXPAND);
  3237.     if (!ev) {
  3238.         if (onlyfails)
  3239.             return (result);
  3240.         JumpOrigFunc(0);
  3241.     }
  3242.     
  3243.     /*
  3244.      *        Allocated event safely, now tell SnoopDos task to wake up
  3245.      */
  3246.     if (onlyfails) {
  3247.         CheckForPause(NULL, 0);
  3248.     } else {
  3249.         ev->status = ES_UPDATING;
  3250.         CheckForPause(ev, seqnum);
  3251.         Signal(SnoopTask, NewEventMask);
  3252.         result = origfunc(name, buffer, size, flags, libbase);
  3253.     }
  3254.     ObtainSemaphore(&BufSem);
  3255.     if (seqnum >= RealFirstSeq) {
  3256.         if (result)
  3257.             ev->result = MSG(MSG_RES_OKAY);
  3258.         else
  3259.             ev->result = MSG(MSG_RES_FAIL);
  3260.         ev->status = ES_READY;
  3261.         Signal(SnoopTask, NewEventMask);
  3262.     }
  3263.     ReleaseSemaphore(&BufSem);
  3264.     return (result);
  3265. }
  3266.  
  3267. /*
  3268.  *        New_DeleteVar()
  3269.  *
  3270.  *        Deletes an environment variable from the environment
  3271.  */
  3272. ULONG ASM New_DeleteVar(reg_d1 char *name, reg_d2 ULONG flags,
  3273.                          reg_a6 void *libbase)
  3274. {
  3275.     MarkCallAddr;
  3276.     FP_DeleteVar origfunc = (FP_DeleteVar)PatchList[GID_SETVAR2].origfunc;
  3277.     int onlyfails = OnlyShowFails;
  3278.     Event *ev;
  3279.     ULONG result;
  3280.     ULONG optid;
  3281.     LONG  seqnum;
  3282.  
  3283.     if (onlyfails) {
  3284.         result = origfunc(name, flags, libbase);
  3285.         if (result)
  3286.             return (result);
  3287.     }
  3288.     /*
  3289.      *        Now determine our flags. We recognise global, local
  3290.      *        or alias variables.
  3291.      */
  3292.     if        (flags & GVF_GLOBAL_ONLY)    optid = MSG_OPT_GLOBAL;
  3293.     else if ((flags & 7) == LV_VAR)        optid = MSG_OPT_LOCAL;
  3294.     else if ((flags & 7) == LV_ALIAS)    optid = MSG_OPT_ALIAS;
  3295.     else                                optid = MSG_OPT_UNKNOWN;
  3296.  
  3297.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_KILLVAR,
  3298.                      name, MSG(optid), NO_EXPAND);
  3299.     if (!ev) {
  3300.         if (onlyfails)
  3301.             return (result);
  3302.         JumpOrigFunc(0);
  3303.     }
  3304.     
  3305.     /*
  3306.      *        Allocated event safely, now tell SnoopDos task to wake up
  3307.      */
  3308.     if (onlyfails) {
  3309.         CheckForPause(NULL, 0);
  3310.     } else {
  3311.         ev->status = ES_UPDATING;
  3312.         CheckForPause(ev, seqnum);
  3313.         Signal(SnoopTask, NewEventMask);
  3314.         result = origfunc(name, flags, libbase);
  3315.     }
  3316.     ObtainSemaphore(&BufSem);
  3317.     if (seqnum >= RealFirstSeq) {
  3318.         if (result)
  3319.             ev->result = MSG(MSG_RES_OKAY);
  3320.         else
  3321.             ev->result = MSG(MSG_RES_FAIL);
  3322.         ev->status = ES_READY;
  3323.         Signal(SnoopTask, NewEventMask);
  3324.     }
  3325.     ReleaseSemaphore(&BufSem);
  3326.     return (result);
  3327. }
  3328.  
  3329. /*
  3330.  *        New_LoadSeg()
  3331.  *
  3332.  *        Tries to load in a module from disk
  3333.  */
  3334. ULONG ASM New_LoadSeg(reg_d1 char *name, reg_a6 void *libbase)
  3335. {
  3336.     MarkCallAddr;
  3337.     FP_LoadSeg origfunc = (FP_LoadSeg)PatchList[GID_LOADSEG].origfunc;
  3338.     int onlyfails = OnlyShowFails;
  3339.     Event *ev;
  3340.     ULONG result;
  3341.     LONG  seqnum;
  3342.  
  3343.     if (onlyfails) {
  3344.         result = origfunc(name, libbase);
  3345.         if (result)
  3346.             return (result);
  3347.     }
  3348.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_LOADSEG,
  3349.                      name, NULL, EXPAND_NAME);
  3350.     if (!ev) {
  3351.         if (onlyfails)
  3352.             return (result);
  3353.         JumpOrigFunc(0);
  3354.     }
  3355.     
  3356.     /*
  3357.      *        Allocated event safely, now tell SnoopDos task to wake up
  3358.      */
  3359.     if (onlyfails) {
  3360.         CheckForPause(NULL, 0);
  3361.     } else {
  3362.         ev->status = ES_UPDATING;
  3363.         CheckForPause(ev, seqnum);
  3364.         
  3365. #if MONITOR_SEMAPHORE
  3366.         LoadTask = SysBase->ThisTask;
  3367.         result = origfunc(name, libbase);
  3368.         LoadTask = 0;
  3369. #else
  3370.         result = origfunc(name, libbase);
  3371. #endif
  3372.     }
  3373.     ObtainSemaphore(&BufSem);
  3374.     if (seqnum >= RealFirstSeq) {
  3375.         if (result)
  3376.             ev->result = MSG(MSG_RES_OKAY);
  3377.         else
  3378.             ev->result = MSG(MSG_RES_FAIL);
  3379.         ev->status = ES_READY;
  3380.         Signal(SnoopTask, NewEventMask);
  3381.     }
  3382.     ReleaseSemaphore(&BufSem);
  3383.     return (result);
  3384. }
  3385.  
  3386. /*
  3387.  *        New_NewLoadSeg()
  3388.  *
  3389.  *        Tries to load in a module from disk
  3390.  */
  3391. ULONG ASM New_NewLoadSeg(reg_d1 char *name, reg_d2 TAGPTR tags,
  3392.                          reg_a6 void *libbase)
  3393. {
  3394.     MarkCallAddr;
  3395.     FP_NewLoadSeg origfunc = (FP_NewLoadSeg)PatchList[GID_LOADSEG2].origfunc;
  3396.     int onlyfails = OnlyShowFails;
  3397.     Event *ev;
  3398.     ULONG result;
  3399.     LONG  seqnum;
  3400.  
  3401.     if (onlyfails) {
  3402.         result = origfunc(name, tags, libbase);
  3403.         if (result)
  3404.             return (result);
  3405.     }
  3406.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_NEWLOADSEG,
  3407.                      name, NULL, EXPAND_NAME);
  3408.     if (!ev) {
  3409.         if (onlyfails)
  3410.             return (result);
  3411.         JumpOrigFunc(0);
  3412.     }
  3413.     
  3414.     /*
  3415.      *        Allocated event safely, now tell SnoopDos task to wake up
  3416.      */
  3417.     if (onlyfails) {
  3418.         CheckForPause(NULL, 0);
  3419.     } else {
  3420.         ev->status = ES_UPDATING;
  3421.         CheckForPause(ev, seqnum);
  3422.         Signal(SnoopTask, NewEventMask);
  3423.         result = origfunc(name, tags, libbase);
  3424.     }
  3425.     ObtainSemaphore(&BufSem);
  3426.     if (seqnum >= RealFirstSeq) {
  3427.         if (result)
  3428.             ev->result = MSG(MSG_RES_OKAY);
  3429.         else
  3430.             ev->result = MSG(MSG_RES_FAIL);
  3431.         ev->status = ES_READY;
  3432.         Signal(SnoopTask, NewEventMask);
  3433.     }
  3434.     ReleaseSemaphore(&BufSem);
  3435.     return (result);
  3436. }
  3437.  
  3438. /*
  3439.  *        New_NewLoadSeg()
  3440.  *
  3441.  *        Tries to load in a module from disk
  3442.  */
  3443. ULONG ASM New_Lock(reg_d1 char *name, reg_d2 LONG mode, reg_a6 void *libbase)
  3444. {
  3445.     MarkCallAddr;
  3446.     FP_Lock origfunc = (FP_Lock)PatchList[GID_LOCKFILE].origfunc;
  3447.     int onlyfails = OnlyShowFails;
  3448.     Event *ev;
  3449.     ULONG result;
  3450.     LONG optid;
  3451.     char *curname;
  3452.     LONG  seqnum;
  3453.  
  3454.     if (onlyfails) {
  3455.         result = origfunc(name, mode, libbase);
  3456.         if (result)
  3457.             return (result);
  3458.     }
  3459.     if        (mode == ACCESS_READ)        optid = MSG_OPT_READ;
  3460.     else if (mode == ACCESS_WRITE)        optid = MSG_OPT_WRITE;
  3461.     else                                optid = MSG_OPT_READBAD;
  3462.  
  3463.     curname = name;
  3464.     if (!ShowPaths && *curname == '\0')
  3465.         curname = "\"\"";
  3466.  
  3467.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_LOCK,
  3468.                      curname, MSG(optid), EXPAND_NAME);
  3469.     if (!ev) {
  3470.         if (onlyfails)
  3471.             return (result);
  3472.         JumpOrigFunc(0);
  3473.     }
  3474.     
  3475.     /*
  3476.      *        Allocated event safely, now tell SnoopDos task to wake up
  3477.      */
  3478.     if (onlyfails) {
  3479.         CheckForPause(NULL, 0);
  3480.     } else {
  3481.         ev->status = ES_UPDATING;
  3482.         CheckForPause(ev, seqnum);
  3483.         Signal(SnoopTask, NewEventMask);
  3484.         result = origfunc(name, mode, libbase);
  3485.     }
  3486.     ObtainSemaphore(&BufSem);
  3487.     if (seqnum >= RealFirstSeq) {
  3488.         if (result)
  3489.             ev->result = MSG(MSG_RES_OKAY);
  3490.         else
  3491.             ev->result = MSG(MSG_RES_FAIL);
  3492.         ev->status = ES_READY;
  3493.         Signal(SnoopTask, NewEventMask);
  3494.     }
  3495.     ReleaseSemaphore(&BufSem);
  3496.     return (result);
  3497. }
  3498.  
  3499. /*
  3500.  *        New_CreateDir()
  3501.  *
  3502.  *        Creates a new directory on disk
  3503.  */
  3504. ULONG ASM New_CreateDir(reg_d1 char *name, reg_a6 void *libbase)
  3505. {
  3506.     MarkCallAddr;
  3507.     FP_CreateDir origfunc = (FP_CreateDir)PatchList[GID_MAKEDIR].origfunc;
  3508.     int onlyfails = OnlyShowFails;
  3509.     Event *ev;
  3510.     ULONG result;
  3511.     LONG  seqnum;
  3512.  
  3513.     if (onlyfails) {
  3514.         result = origfunc(name, libbase);
  3515.         if (result)
  3516.             return (result);
  3517.     }
  3518.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_MAKEDIR,
  3519.                      name, NULL, EXPAND_NAME);
  3520.     if (!ev) {
  3521.         if (onlyfails)
  3522.             return (result);
  3523.         JumpOrigFunc(0);
  3524.     }
  3525.     
  3526.     /*
  3527.      *        Allocated event safely, now tell SnoopDos task to wake up
  3528.      */
  3529.     if (onlyfails) {
  3530.         CheckForPause(NULL, 0);
  3531.     } else {
  3532.         ev->status = ES_UPDATING;
  3533.         CheckForPause(ev, seqnum);
  3534.         Signal(SnoopTask, NewEventMask);
  3535.         result = origfunc(name, libbase);
  3536.     }
  3537.     ObtainSemaphore(&BufSem);
  3538.     if (seqnum >= RealFirstSeq) {
  3539.         if (result)
  3540.             ev->result = MSG(MSG_RES_OKAY);
  3541.         else
  3542.             ev->result = MSG(MSG_RES_FAIL);
  3543.         ev->status = ES_READY;
  3544.         Signal(SnoopTask, NewEventMask);
  3545.     }
  3546.     ReleaseSemaphore(&BufSem);
  3547.     return (result);
  3548. }
  3549.  
  3550. /*
  3551.  *        New_MakeLink()
  3552.  *
  3553.  *        Creates a new hard or soft link
  3554.  */
  3555. ULONG ASM New_MakeLink(reg_d1 char *name, reg_d2 LONG dest, reg_d3 LONG soft,
  3556.                         reg_a6 void *libbase)
  3557. {
  3558.     MarkCallAddr;
  3559.     struct Process *myproc = (struct Process *)SysBase->ThisTask;
  3560.     FP_MakeLink origfunc   = (FP_MakeLink)PatchList[GID_MAKELINK].origfunc;
  3561.     int onlyfails          = OnlyShowFails;
  3562.     Event *ev;
  3563.     ULONG result;
  3564.     LONG optid;
  3565.     char namestr[MAX_STR_LEN+1];
  3566.     LONG seqnum;
  3567.     int  len;
  3568.  
  3569.     if (onlyfails) {
  3570.         result = origfunc(name, dest, soft, libbase);
  3571.         if (result)
  3572.             return (result);
  3573.     }
  3574.     if (soft)    optid = MSG_OPT_SOFTLINK;
  3575.     else        optid = MSG_OPT_HARDLINK;
  3576.  
  3577.     /*
  3578.      *        Now build a string that looks like "name --> <dest>" to display
  3579.      *        as our link. For soft links, we just concatenate the two
  3580.      *        strings. For hard links, we have to generate a filename
  3581.      *        from the lock also.
  3582.      */
  3583.     len = strlen(name);
  3584.     if (len >= MAX_STR_LEN) {
  3585.         strncpy(namestr, name, MAX_STR_LEN);
  3586.         namestr[MAX_STR_LEN] = 0;
  3587.     } else {
  3588.         if (ShowPaths) {
  3589.             strcpy(namestr, MyNameFromLock(myproc->pr_CurrentDir,
  3590.                                            name, namestr, MAX_STR_LEN-2));
  3591.             len = strlen(namestr);
  3592.         } else
  3593.             strcpy(namestr, name);
  3594.  
  3595.         strcat(namestr, LinkPointerString);        /* Usually ' --> ' */
  3596.         if (soft) {
  3597.             strncat(namestr, (char *)dest, MAX_STR_LEN - len - 1);
  3598.             namestr[MAX_STR_LEN] = 0;
  3599.         } else {
  3600.             strcat(namestr, MyNameFromLock(dest, NULL, namestr+len+1,
  3601.                                                  MAX_STR_LEN-len-1));
  3602.         }
  3603.     }
  3604.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_MAKELINK,
  3605.                      namestr, MSG(optid), NO_EXPAND);
  3606.     if (!ev) {
  3607.         if (onlyfails)
  3608.             return (result);
  3609.         JumpOrigFunc(0);
  3610.     }
  3611.     
  3612.     /*
  3613.      *        Allocated event safely, now tell SnoopDos task to wake up
  3614.      */
  3615.     if (onlyfails) {
  3616.         CheckForPause(NULL, 0);
  3617.     } else {
  3618.         ev->status = ES_UPDATING;
  3619.         CheckForPause(ev, seqnum);
  3620.         Signal(SnoopTask, NewEventMask);
  3621.         result = origfunc(name, dest, soft, libbase);
  3622.     }
  3623.     ObtainSemaphore(&BufSem);
  3624.     if (seqnum >= RealFirstSeq) {
  3625.         if (result)
  3626.             ev->result = MSG(MSG_RES_OKAY);
  3627.         else
  3628.             ev->result = MSG(MSG_RES_FAIL);
  3629.         ev->status = ES_READY;
  3630.         Signal(SnoopTask, NewEventMask);
  3631.     }
  3632.     ReleaseSemaphore(&BufSem);
  3633.     return (result);
  3634. }
  3635.  
  3636. /*
  3637.  *        New_Open()
  3638.  *
  3639.  *        Opens a new file on disk
  3640.  */
  3641. ULONG ASM New_Open(reg_d1 char *name, reg_d2 LONG accessMode,
  3642.                   reg_a6 void *libbase)
  3643. {
  3644.     MarkCallAddr;
  3645.     FP_Open origfunc = (FP_Open)PatchList[GID_OPENFILE].origfunc;
  3646.     int onlyfails = OnlyShowFails;
  3647.     Event *ev;
  3648.     ULONG result;
  3649.     char *optstr;
  3650.     LONG  seqnum;
  3651.  
  3652.     if        (accessMode == MODE_OLDFILE)    optstr = MSG(MSG_OPT_READ);
  3653.     else if (accessMode == MODE_NEWFILE)    optstr = MSG(MSG_OPT_WRITE);
  3654.     else if (accessMode == MODE_READWRITE)    optstr = MSG(MSG_OPT_MODIFY);
  3655.     else                                    optstr = MSG(MSG_OPT_UNKNOWN);
  3656.  
  3657.     if (onlyfails) {
  3658.         result = origfunc(name, accessMode, libbase);
  3659.         if (result)
  3660.             return (result);
  3661.     }
  3662.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_OPEN,
  3663.                      name, optstr, EXPAND_NAME);
  3664.     if (!ev) {
  3665.         if (onlyfails)
  3666.             return (result);
  3667.         JumpOrigFunc(0);
  3668.     }
  3669.     
  3670.     /*
  3671.      *        Allocated event safely, now tell SnoopDos task to wake up
  3672.      */
  3673.     if (onlyfails) {
  3674.         CheckForPause(NULL, 0);
  3675.     } else {
  3676.         ev->status = ES_UPDATING;
  3677.         CheckForPause(ev, seqnum);
  3678.         Signal(SnoopTask, NewEventMask);
  3679.         result = origfunc(name, accessMode, libbase);
  3680.     }
  3681.     ObtainSemaphore(&BufSem);
  3682.     if (seqnum >= RealFirstSeq) {
  3683.         if (result)
  3684.             ev->result = MSG(MSG_RES_OKAY);
  3685.         else
  3686.             ev->result = MSG(MSG_RES_FAIL);
  3687.         ev->status = ES_READY;
  3688.         Signal(SnoopTask, NewEventMask);
  3689.     }
  3690.     ReleaseSemaphore(&BufSem);
  3691.     return (result);
  3692. }
  3693.  
  3694. /*
  3695.  *        New_Rename()
  3696.  *
  3697.  *        Renames a file on disk. This is a little tricky, since we have
  3698.  *        to generate two events: one containing the source name and
  3699.  *        one containing the destination name.
  3700.  */
  3701. ULONG ASM New_Rename(reg_d1 char *oldname, reg_d2 char *newname,
  3702.                      reg_a6 void *libbase)
  3703. {
  3704.     MarkCallAddr;
  3705.     FP_Rename origfunc = (FP_Rename)PatchList[GID_RENAME].origfunc;
  3706.     int onlyfails = OnlyShowFails;
  3707.     Event *ev;
  3708.     ULONG result;
  3709.     LONG  seqnum;
  3710.  
  3711.     if (onlyfails) {
  3712.         result = origfunc(oldname, newname, libbase);
  3713.         if (result)
  3714.             return (result);
  3715.     }
  3716.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_RENAME,
  3717.                      oldname, NULL, EXPAND_NAME);
  3718.     if (!ev) {
  3719.         if (onlyfails)
  3720.             return (result);
  3721.         JumpOrigFunc(0);
  3722.     }
  3723.     ev->status = ES_READY;
  3724.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_RENAME2,
  3725.                      newname, NULL, EXPAND_NAME);
  3726.     if (!ev) {
  3727.         if (onlyfails)
  3728.             return (result);
  3729.         JumpOrigFunc(0);
  3730.     }
  3731.     
  3732.     /*
  3733.      *        Allocated event safely, now tell SnoopDos task to wake up
  3734.      */
  3735.     if (onlyfails) {
  3736.         CheckForPause(NULL, 0);
  3737.     } else {
  3738.         ev->status = ES_UPDATING;
  3739.         CheckForPause(ev, seqnum);
  3740.         Signal(SnoopTask, NewEventMask);
  3741.         result = origfunc(oldname, newname, libbase);
  3742.     }
  3743.     ObtainSemaphore(&BufSem);
  3744.     if (seqnum >= RealFirstSeq) {
  3745.         if (result)
  3746.             ev->result = MSG(MSG_RES_OKAY);
  3747.         else
  3748.             ev->result = MSG(MSG_RES_FAIL);
  3749.         ev->status = ES_READY;
  3750.         Signal(SnoopTask, NewEventMask);
  3751.     }
  3752.     ReleaseSemaphore(&BufSem);
  3753.     return (result);
  3754. }
  3755.  
  3756. /*
  3757.  *        New_RunCommand()
  3758.  *
  3759.  *        Runs a command from disk. Like Execute() only with more control.
  3760.  */
  3761. ULONG ASM New_RunCommand(reg_d1 BPTR seglist,  reg_d2 ULONG stack,
  3762.                          reg_d3 char *cmdline, reg_d4 cmdlen,
  3763.                           reg_a6 void *libbase)
  3764. {
  3765.     MarkCallAddr;
  3766.     FP_RunCommand origfunc = (FP_RunCommand)PatchList[GID_RUNCOMMAND].origfunc;
  3767.     int onlyfails = OnlyShowFails;
  3768.     Event *ev;
  3769.     ULONG result;
  3770.     char  stacksize[40];    /* Reserve additional space for return code! */
  3771.     LONG  seqnum;
  3772.     int      stklen;
  3773.     char  *p;
  3774.  
  3775.     /*
  3776.      *        For functions that may potentially take a long time to complete,
  3777.      *        we check if they're being called from ROM straight away, so
  3778.      *        that we can exit and not hang around waiting for them if they
  3779.      *        are, even in the OnlyShowFails case.
  3780.      */
  3781.     if (!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd)
  3782.         JumpOrigFunc(0);
  3783.  
  3784.     if (onlyfails) {
  3785.         result = origfunc(seglist, stack, cmdline, cmdlen, libbase);
  3786.         if (result != -1)
  3787.             return (result);
  3788.     }
  3789.  
  3790.     /*
  3791.      *        This is kind of nasty but it works. We need somewhere to store
  3792.      *        the return code string, but when we call CreateEvent(), we
  3793.      *        don't know what it will be yet. So, we allocate additional
  3794.      *        space at the end of our stacksize to hold this info, and
  3795.      *        overwrite it later on.
  3796.      */
  3797.     mysprintf(stacksize, "%ld", stack);
  3798.     stklen = strlen(stacksize);
  3799.     strcat(stacksize, "................");    /* Storage for return code */
  3800.  
  3801.     /*
  3802.      *        Next, a quick hack to get rid of that nasty CR at the end
  3803.      *        of the string (purely for visual purposes -- we put it back
  3804.      *        afterwards, of course!)
  3805.      */
  3806.     for (p = cmdline; *p; p++) {
  3807.         if (*p == '\n') {
  3808.             *p = ' ';
  3809.             break;
  3810.         }
  3811.     }
  3812.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_RUNCOMMAND,
  3813.                      cmdline, stacksize, NO_EXPAND);
  3814.     if (*p)
  3815.         *p = '\n';
  3816.  
  3817.     if (!ev) {
  3818.         if (onlyfails)
  3819.             return (result);
  3820.         JumpOrigFunc(0);
  3821.     }
  3822.     ev->options[stklen] = 0;
  3823.     
  3824.     /*
  3825.      *        Allocated event safely, now tell SnoopDos task to wake up
  3826.      */
  3827.     if (onlyfails) {
  3828.         CheckForPause(NULL, 0);
  3829.     } else {
  3830.         ev->result = "----";
  3831.         ev->status = ES_UPDATING;
  3832.         CheckForPause(ev, seqnum);
  3833.         Signal(SnoopTask, NewEventMask);
  3834.         result = origfunc(seglist, stack, cmdline, cmdlen, libbase);
  3835.     }
  3836.     ObtainSemaphore(&BufSem);
  3837.     if (seqnum >= RealFirstSeq) {
  3838.         if (result != -1) {
  3839.             ev->result = ev->options + stklen + 1;
  3840.             mysprintf(ev->result, "%ld", result);
  3841.         } else {
  3842.             ev->result = MSG(MSG_RES_FAIL);
  3843.         }
  3844.         ev->status = ES_READY;
  3845.         Signal(SnoopTask, NewEventMask);
  3846.     }
  3847.     ReleaseSemaphore(&BufSem);
  3848.     return (result);
  3849. }
  3850.  
  3851. /*
  3852.  *        New_SystemTagList()
  3853.  *
  3854.  *        Executes a command line. Like Execute() only more powerful.
  3855.  */
  3856. ULONG ASM New_SystemTagList(reg_d1 char *cmdline, reg_d2 TAGPTR tags,
  3857.                              reg_a6 void *libbase)
  3858. {
  3859.     MarkCallAddr;
  3860.     FP_SystemTagList origfunc = (FP_SystemTagList)
  3861.                                  PatchList[GID_SYSTEM].origfunc;
  3862.     int onlyfails = OnlyShowFails;
  3863.     Event *ev;
  3864.     ULONG result;
  3865.     char  optstr[20];    /* Reserve additional space for return code! */
  3866.     LONG  seqnum;
  3867.  
  3868.     /*
  3869.      *        For functions that may potentially take a long time to complete,
  3870.      *        we check if they're being called from ROM straight away, so
  3871.      *        that we can exit and not hang around waiting for them if they
  3872.      *        are, even in the OnlyShowFails case.
  3873.      */
  3874.     if (!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd)
  3875.         JumpOrigFunc(0);
  3876.  
  3877.     if (onlyfails) {
  3878.         result = origfunc(cmdline, tags, libbase);
  3879.         if (result != -1)
  3880.             return (result);
  3881.     }
  3882.     /*
  3883.      *        This is kind of nasty but it works. We need somewhere to store
  3884.      *        the return code string, but when we call CreateEvent(), we
  3885.      *        don't know what it will be yet. So, we allocate space for our
  3886.      *        option string instead and then replace it with our result
  3887.      *        string later on.
  3888.      */
  3889.     strcpy(optstr, "............");    /* Storage for result string */
  3890.  
  3891.     ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_SYSTEM, cmdline,
  3892.                      optstr, NO_EXPAND);
  3893.     if (!ev) {
  3894.         if (onlyfails)
  3895.             return (result);
  3896.         JumpOrigFunc(0);
  3897.     }
  3898.     *ev->options = '\0';
  3899.     
  3900.     /*
  3901.      *        Allocated event safely, now tell SnoopDos task to wake up
  3902.      */
  3903.     if (onlyfails) {
  3904.         CheckForPause(NULL, 0);
  3905.     } else {
  3906.         ev->result = "----";
  3907.         ev->status = ES_UPDATING;
  3908.         CheckForPause(ev, seqnum);
  3909.         Signal(SnoopTask, NewEventMask);
  3910.         result = origfunc(cmdline, tags, libbase);
  3911.     }
  3912.     ObtainSemaphore(&BufSem);
  3913.     if (seqnum >= RealFirstSeq) {
  3914.         if (result != -1) {
  3915.             ev->result  = ev->options;
  3916.             ev->options = "";
  3917.             mysprintf(ev->result, "%ld", result);
  3918.         } else {
  3919.             ev->result = MSG(MSG_RES_FAIL);
  3920.         }
  3921.         ev->status = ES_READY;
  3922.         Signal(SnoopTask, NewEventMask);
  3923.     }
  3924.     ReleaseSemaphore(&BufSem);
  3925.     return (result);
  3926. }
  3927.  
  3928. /*****************************************************************************
  3929.  *
  3930.  *      Now the Mother of all patches -- the PutMsg() packet monitor/Rexx patch
  3931.  *
  3932.  *      (There are quite a few sub-functions associated with this.)
  3933.  *
  3934.  *****************************************************************************/
  3935.  
  3936. /*
  3937.  *        New_AddDosEntry(dlist)
  3938.  *
  3939.  *        This is a very simple patch which is only enabled when we are
  3940.  *        monitoring DOS packets. It simply signals the main task to update
  3941.  *        the device list whenever a new device is added to the DOS device
  3942.  *        list.
  3943.  */
  3944. ULONG ASM New_AddDosEntry(reg_d1 struct DosList *dlist, reg_a6 void *libbase)
  3945. {
  3946.     FP_AddDosEntry origfunc = (FP_AddDosEntry)
  3947.                                PatchList[GID_ADDDOSENTRY].origfunc;
  3948.     ULONG result;
  3949.  
  3950.     /*
  3951.      *        Note that we better remember to actually add the new device
  3952.      *        before telling SnoopDos to update its list, otherwise it
  3953.      *        might miss it if it's too quick (e.g. priority > 0).
  3954.      *
  3955.      *        New: Even though we DO add it before signalling SnoopDos, the
  3956.      *        device itself won't be fully initialised for a little bit longer.
  3957.      *        Therefore, the main SnoopDos code 
  3958.      */
  3959.     result = origfunc(dlist, libbase);
  3960.  
  3961.     if (dlist && dlist->dol_Type == DLT_DEVICE)
  3962.         Signal(SnoopTask, ScanDosListMask);
  3963.     
  3964.     return (result);
  3965. }
  3966.  
  3967. /*
  3968.  *        AddWaitingPacket(ev, seqnum, failmsg, dp, port, pt)
  3969.  *
  3970.  *        Adds a new entry to the list of waiting packets containing enough
  3971.  *        information to allow us to identify the reply to the packet when
  3972.  *        it is processed later on (in the main PutMsg() patch).
  3973.  *
  3974.  *        If there isn't a spare waiting packet, we will steal one and mark
  3975.  *        the event it represented as "Miss" to indicate the return code
  3976.  *        was missed.
  3977.  */
  3978. void AddWaitingPacket(Event *ev, LONG seqnum, char *failmsg,
  3979.                       struct DosPacket *dp, struct MsgPort *port,
  3980.                       struct PacketRef *pt)
  3981. {
  3982.     WaitPacket *wp;
  3983.     Event *oldev;
  3984.     ULONG oldseq;
  3985.  
  3986.     /*
  3987.      *        Add details about this packet to our list of waiting
  3988.      *        packets, so that when we spot the packet being returned
  3989.      *        by PutMsg() later on, we can fill in the result code.
  3990.      *
  3991.      *        Note that we must not obtain both the buffer and packet
  3992.      *        semaphores simultaneously, or a deadlock could occur.
  3993.      */
  3994.     ObtainSemaphore(&PacketSem);
  3995.     if (IsListEmpty(&PacketFreeList)) {
  3996.         /*
  3997.          *        Removing an existing node which is already in use. We need
  3998.          *        to signal our main task to tell it not to continue waiting
  3999.          *        for this event to complete. We have to wait until we unlock
  4000.          *        the packetsem so we just note the details for now.
  4001.          */
  4002.         wp     = (WaitPacket *)RemTail(&PacketWaitList);
  4003.         oldseq = wp->eventnum;
  4004.         oldev  = wp->event;
  4005.         //    if (!wp)
  4006.         //        KPrintF("Warning!! No packet available on WaitList!\n");
  4007.     } else {
  4008.         wp     = (WaitPacket *)RemHead(&PacketFreeList);
  4009.         oldseq = 0;
  4010.     }
  4011.     wp->dp        = dp;
  4012.     wp->sendtask = SysBase->ThisTask;
  4013.     wp->destport = port;
  4014.     wp->event    = ev;
  4015.     wp->eventnum = seqnum;
  4016.     wp->arg1      = dp->dp_Arg1;
  4017.     wp->arg2      = dp->dp_Arg2;
  4018.     wp->arg3      = dp->dp_Arg3;
  4019.     wp->resmsg     = failmsg;
  4020.     wp->flags    = pt->flags;
  4021.     AddHead(&PacketWaitList, (Node *)wp);
  4022.     ReleaseSemaphore(&PacketSem);
  4023.  
  4024.     /*
  4025.      *        Finally, check if we had to flush out an old packet from
  4026.      *        our list of waiting packets when we allocated the new
  4027.      *        packet. If so, mark the packet as missed (if it hasn't
  4028.      *        already scrolled off the top of the buffer)
  4029.      */
  4030.     if (oldseq) {
  4031.         ObtainSemaphore(&BufSem);
  4032.         if (oldseq >= RealFirstSeq) {
  4033.             oldev->result = MSG(MSG_RES_MISSED);
  4034.             oldev->status = ES_READY;
  4035.             Signal(SnoopTask, NewEventMask);
  4036.         }
  4037.         ReleaseSemaphore(&BufSem);
  4038.     }
  4039. }
  4040.  
  4041. /*
  4042.  *        HandleSimplePacket(calladdr, packet, port, packetref)
  4043.  *
  4044.  *        Handles the given packet by creating an event that matches the
  4045.  *        packet type (if we support it) and creating a new entry on the
  4046.  *        waiting packet list so we can match it up later.
  4047.  *
  4048.  *        This function handles only simple packets (i.e. those with the
  4049.  *        PK_COMMON flag set in the packet table). If the corresponding
  4050.  *        AmigaDOS option (Open, Lock, MakeDir etc.) is not currently
  4051.  *        enabled, then no entry is made for this packet.
  4052.  */
  4053. void HandleSimplePacket(ULONG calladdr, struct DosPacket *dp,
  4054.                          struct MsgPort *port, struct PacketRef *pt)
  4055. {
  4056.     /*
  4057.      *        Now create our new simple event to represent the packet.
  4058.      *        The contents of this will vary depending on the packet
  4059.      *        itself, but in general, is an exact match with the format
  4060.      *        of the AmigaDOS function associated with the packet.
  4061.      *
  4062.      *        (If that function is not being monitored, then we do nothing.)
  4063.      */
  4064.     Event *ev;
  4065.     LONG  seqnum;
  4066.     ULONG gadid;
  4067.     ULONG actid = 0;
  4068.     char  *volname = ((Task *)(port->mp_SigTask))->tc_Node.ln_Name;
  4069.     BPTR  lock = NULL;
  4070.     BSTR  name;
  4071.     char  *optstr;
  4072.     char  namebuf[MAX_STR_LEN+1];
  4073.     char  optbuf[20];
  4074.  
  4075.     namebuf[0] = '\0';
  4076.  
  4077.     /*
  4078.      *        First, determine the format to use, based on the packet type
  4079.      */
  4080.     switch (dp->dp_Type) {
  4081.         case ACTION_FINDINPUT:
  4082.             gadid    = GID_OPENFILE;
  4083.             actid    = MSG_ACT_POPEN;
  4084.             lock    = dp->dp_Arg2;
  4085.             name    = dp->dp_Arg3;
  4086.             optstr    = MSG(MSG_OPT_READ);
  4087.             break;
  4088.  
  4089.         case ACTION_FINDOUTPUT:
  4090.             gadid    = GID_OPENFILE;
  4091.             actid    = MSG_ACT_POPEN;
  4092.             lock    = dp->dp_Arg2;
  4093.             name    = dp->dp_Arg3;
  4094.             optstr    = MSG(MSG_OPT_WRITE);
  4095.             break;
  4096.  
  4097.         case ACTION_FINDUPDATE:
  4098.             gadid    = GID_OPENFILE;
  4099.             actid    = MSG_ACT_POPEN;
  4100.             lock    = dp->dp_Arg2;
  4101.             name    = dp->dp_Arg3;
  4102.             optstr    = MSG(MSG_OPT_MODIFY);
  4103.             break;
  4104.  
  4105.         case ACTION_DELETE_OBJECT:
  4106.             gadid    = GID_DELETE;
  4107.             actid    = MSG_ACT_PDELETE;
  4108.             lock    = dp->dp_Arg1;
  4109.             name    = dp->dp_Arg2;
  4110.             optstr  = NULL;
  4111.             break;
  4112.  
  4113.         case ACTION_CREATE_DIR:
  4114.             gadid    = GID_MAKEDIR;
  4115.             actid    = MSG_ACT_PMAKEDIR;
  4116.             lock    = dp->dp_Arg1;
  4117.             name    = dp->dp_Arg2;
  4118.             optstr  = NULL;
  4119.             break;
  4120.  
  4121.         case ACTION_LOCATE_OBJECT:
  4122.             gadid    = GID_LOCKFILE;
  4123.             actid    = MSG_ACT_PLOCK;
  4124.             lock    = dp->dp_Arg1;
  4125.             name    = dp->dp_Arg2;
  4126.             switch (dp->dp_Arg3) {
  4127.                 case ACCESS_READ:    optstr = MSG(MSG_OPT_READ);        break;
  4128.                 case ACCESS_WRITE:    optstr = MSG(MSG_OPT_WRITE);    break;
  4129.                 default:            optstr = MSG(MSG_OPT_READBAD);    break;
  4130.             }
  4131.             break;
  4132.  
  4133.         case ACTION_MAKE_LINK:
  4134.         {
  4135.             int len;
  4136.             char *cname;
  4137.  
  4138.             gadid    = GID_MAKELINK;
  4139.             actid    = MSG_ACT_PMAKELINK;
  4140.             lock    = dp->dp_Arg1;
  4141.             cname    = BTOC(dp->dp_Arg2);
  4142.  
  4143.             /*
  4144.              *        Now build a string that looks like "name --> <dest>" to
  4145.              *        display as our link. For soft links, we just concatenate
  4146.              *        the two strings. For hard links, we have to generate a
  4147.              *        filename from the lock also.
  4148.              */
  4149.             len = *cname;
  4150.             if (len >= MAX_STR_LEN) {
  4151.                 strncpy(namebuf, cname+1, MAX_STR_LEN);
  4152.                 namebuf[MAX_STR_LEN] = 0;
  4153.             } else {
  4154.                 if (ShowPaths) {
  4155.                     char *tempbuf = namebuf + MAX_STR_LEN-9 - len;
  4156.  
  4157.                     memcpy(tempbuf, cname+1, len);
  4158.                     tempbuf[len] = '\0';
  4159.                     strcpy(namebuf, AsyncNameFromLock(volname, lock, tempbuf,
  4160.                                                       namebuf, MAX_STR_LEN-8));
  4161.                     len = strlen(namebuf);
  4162.                 } else {
  4163.                     memcpy(namebuf, cname+1, len);
  4164.                     namebuf[len] = '\0';
  4165.                 }
  4166.                 strcat(namebuf, LinkPointerString);        /* Usually ' --> ' */
  4167.                 len += strlen(LinkPointerString);
  4168.  
  4169.                 if (dp->dp_Arg4 == LINK_HARD) {
  4170.                     /*
  4171.                      *        Hard link: interpret dp->dp_Arg3 as the lock
  4172.                      *        being linked to
  4173.                      */
  4174.                     strcat(namebuf, AsyncNameFromLock(volname, dp->dp_Arg3,
  4175.                                                       NULL, namebuf+len+1,
  4176.                                                       MAX_STR_LEN-len-1));
  4177.                     optstr = MSG(MSG_OPT_HARDLINK);
  4178.                 } else {
  4179.                     /*
  4180.                      *        Soft link: interpret dp->dp_Arg3 as the name of
  4181.                      *        the path to the link
  4182.                      */
  4183.                     strncat(namebuf, (char *)dp->dp_Arg3, MAX_STR_LEN-len-1);
  4184.                     namebuf[MAX_STR_LEN] = 0;
  4185.                     optstr = MSG(MSG_OPT_SOFTLINK);
  4186.                 }
  4187.             }
  4188.             volname = (char *)NO_EXPAND; /* Don't try and expand filename */
  4189.             name    = NULL;
  4190.             break;
  4191.         }
  4192.  
  4193.         case ACTION_RENAME_OBJECT:
  4194.             lock    = dp->dp_Arg1;
  4195.             name    = dp->dp_Arg2;
  4196.             optstr    = NULL;
  4197.  
  4198.             /*
  4199.              *        This is a bit cheeky. The Rename action comes in
  4200.              *        two parts, the first line with the original name and
  4201.              *        the second part with the new name. We create the
  4202.              *        first part here, and then fall through to let the
  4203.              *        rest of the code handle the second part.
  4204.              */
  4205.             if (CurSettings.Func.Opts[GID_RENAME]) {
  4206.                 char *cname = BTOC(name);
  4207.  
  4208.                 lock          = dp->dp_Arg3;
  4209.                 name          = dp->dp_Arg4;
  4210.                 if (*cname) {
  4211.                     memcpy(namebuf, cname+1, *cname);
  4212.                     namebuf[*cname] = '\0';
  4213.                 } else {
  4214.                     /*
  4215.                      *        If we have an empty string, we replace it with ""
  4216.                      *        since we may be renaming the current directory (?)
  4217.                      */
  4218.                     strcpy(namebuf, "\"\"");
  4219.                 }
  4220.                 ev = CreateEvent(calladdr, &seqnum, MSG_ACT_PRENAME,
  4221.                                  namebuf, NULL, (int)volname, lock);
  4222.                 if (!ev)
  4223.                     return;
  4224.  
  4225.                 ev->status    = ES_READY;
  4226.                 gadid        = GID_RENAME;
  4227.                 actid         = MSG_ACT_PRENAME2;
  4228.             }
  4229.             break;
  4230.     }
  4231.  
  4232.     /*
  4233.      *        Now, if we recognised the packet type, create a new event
  4234.      *        with the info stored accordingly. We ignore any packets
  4235.      *        for which monitoring is disabled.
  4236.      */
  4237.     if (actid && CurSettings.Func.Opts[gadid]) {
  4238.         /*
  4239.          *        We reserve 8 bytes at the start of the options string to
  4240.          *        hold the result message (Ok or Fail) that is filled in
  4241.          *        by the PutMsg() code when the packet is returned.
  4242.          */
  4243. #define RES_LEN        8
  4244.  
  4245.         strcpy(optbuf, "........");
  4246.         if (optstr)
  4247.             strcat(optbuf, optstr);
  4248.  
  4249.         if (name) {
  4250.             char *cname = BTOC(name);
  4251.  
  4252.             namebuf[0] = '\0';
  4253.             if (*cname) {
  4254.                 memcpy(namebuf, cname+1, *cname);
  4255.                 namebuf[*cname] = '\0';
  4256.             } else {
  4257.                 /*
  4258.                  *        If we have an empty string, we replace it with ""
  4259.                  *        since it will typically be a Lock("").
  4260.                  */
  4261.                 strcpy(namebuf, "\"\"");
  4262.             }
  4263.         }
  4264.         ev = CreateEvent(calladdr, &seqnum, actid, namebuf, optbuf,
  4265.                          (int)volname, lock);
  4266.         if (!ev)
  4267.             return;
  4268.  
  4269.         ev->result    = ev->options;
  4270.         ev->options  += RES_LEN;
  4271.         *ev->result   = '\0';
  4272.         ev->status    = ES_UPDATING;
  4273.         /*
  4274.          *        We add the event to the waiting list BEFORE checking for Pause
  4275.          *        so that if while paused, the user decides to disable packet
  4276.          *        monitoring, the packet will be in the wait queue already and
  4277.          *        thus will be safely flushed; otherwise, it would hang around
  4278.          *        for ever (not doing any damage but wasting resources).
  4279.          */
  4280.         AddWaitingPacket(ev, seqnum, "%s", dp, port, pt);
  4281.         CheckForPause(ev, seqnum);
  4282.         Signal(SnoopTask, NewEventMask);
  4283.     }
  4284. #undef RES_LEN
  4285. }
  4286.  
  4287. /*
  4288.  *        HandleRawPacket(calladdr, packet, port, packetref)
  4289.  *
  4290.  *        Handles the given packet by creating an event that matches the
  4291.  *        packet type (if we support it) and creating a new entry on the
  4292.  *        waiting packet list so we can match it up later.
  4293.  *
  4294.  *        This function handles all packets and produces a raw hex dump
  4295.  *        (with the name of the packet) rather than the more english-like
  4296.  *        "Open", "Lock", etc.
  4297.  */
  4298. void HandleRawPacket(ULONG calladdr, struct DosPacket *dp,
  4299.                      struct MsgPort *port, struct PacketRef *pt)
  4300. {
  4301.     /*
  4302.      *        Building this event is a little tricky, since the result
  4303.      *        string will not be a static string but will contain the
  4304.      *        actual return value. Thus, we allocate enough room in the
  4305.      *        filename portion to hold the result string as well, and
  4306.      *        adjust our variables accordingly after the event has
  4307.      *        been created.
  4308.      *
  4309.      *        The same is true for raw packet types (i.e. those that we
  4310.      *        don't recognise at all).
  4311.      */
  4312. #define RAW_LEN        20        /* Length of possible raw packet type    */
  4313. #define RES_LEN        26        /* Length of longest result code        */ 
  4314. #define ARG_LEN        50        /* Length of longest argument list        */
  4315.  
  4316.     static char *fmt[] = {
  4317.         "(None)", "%8lx", "%8lx, %8lx", "%8lx, %8lx, %8lx",
  4318.         "%8lx, %8lx, %8lx, %8lx", "%8lx, %8lx, %8lx, %8lx, %8lx"
  4319.     };
  4320.     static char *failmsg[] = {
  4321.         "%s", "%-4s %8lx", "%-4s %8lx", "%-4s %8lx, %8lx"
  4322.     };
  4323.     char args[RAW_LEN+RES_LEN+ARG_LEN];
  4324.     char *tname = args;
  4325.     Event *ev;
  4326.     LONG  seqnum;
  4327.  
  4328.     if (pt->flags & PK_RAW)
  4329.         tname += RAW_LEN;   
  4330.  
  4331.     tname += RES_LEN;
  4332.     memset(args, ' ', tname - args);
  4333.     mysprintf(tname, fmt[pt->numparams], dp->dp_Arg1, dp->dp_Arg2,
  4334.                             dp->dp_Arg3, dp->dp_Arg4, dp->dp_Arg5);
  4335.  
  4336.     ev = CreateEvent(calladdr, &seqnum, pt->msgid, args,
  4337.                      ((Task *)(port->mp_SigTask))->tc_Node.ln_Name,
  4338.                      NO_EXPAND);
  4339.     if (!ev)
  4340.         return;
  4341.  
  4342.     if (pt->flags & PK_RAW) {
  4343.         /*
  4344.          *        Now fix up action name and target name
  4345.          */
  4346.         ev->action    = ev->filename;
  4347.         ev->filename += RAW_LEN;
  4348.         mysprintf(ev->action, MSG(MSG_ACT_RAWPACKET), dp->dp_Type);
  4349.     }
  4350.     ev->result    = ev->filename;    /* Point to storage for later result */
  4351.     ev->filename += RES_LEN;
  4352.     *ev->result   = 0;                /* Make result field empty initially */
  4353.     ev->status      = ES_UPDATING;
  4354.     /*
  4355.      *        We add the event to the waiting list BEFORE checking for Pause
  4356.      *        so that if while paused, the user decides to disable packet
  4357.      *        monitoring, the packet will be in the wait queue already and
  4358.      *        thus will be safely flushed; otherwise, it would hang around
  4359.      *        for ever (not doing any damage but wasting resources).
  4360.      */
  4361.     AddWaitingPacket(ev, seqnum, failmsg[pt->flags & PK_MASK], dp, port, pt);
  4362.     CheckForPause(ev, seqnum);
  4363.     Signal(SnoopTask, NewEventMask);
  4364. }
  4365.  
  4366. /*
  4367.  *        New_PutMsg()
  4368.  *
  4369.  *        This function is used frequently by the system to send a message
  4370.  *        from one task to another. It is also used by applications wishing
  4371.  *        to send ARexx messages, which is what we want to monitor.
  4372.  *
  4373.  *        The problem is: how do we distinguish system messages (of which
  4374.  *        there are lots!) from ARexx messages (which are relatively
  4375.  *        infrequent).
  4376.  *
  4377.  *        It's almost impossible to get a perfect solution to this, but we
  4378.  *        can get fairly close by checking the following:
  4379.  *
  4380.  *          - The destination port must have a name
  4381.  *          - The host name associated with the supposed Rexx message must
  4382.  *            point to valid memory
  4383.  *        - The action type for the Rexx message is RXCOMM
  4384.  *        - The Rexx system library must be up and running
  4385.  *        - The library base must be word aligned (else we fault on a 68000)
  4386.  *        - The Close() vector in the Rexx private system library must
  4387.  *            match the Close() vector in the Rexx library base that we
  4388.  *            opened ourselves (the library bases themselves can be different).
  4389.  *
  4390.  *        If all these hold true, then we can assume it is a valid ARexx
  4391.  *        message and display it accordingly.
  4392.  *
  4393.  *        For DOS packets, things are a little simpler since DOS packets contain
  4394.  *        cross-links that can be easily checked for validity. To keep things
  4395.  *        moving at a reasonable speed, we only consider StandardPackets, where
  4396.  *        the DosPacket structure immediately follows the exec message structure.
  4397.  *        This is not universally true, but it covers all the cases we want to
  4398.  *        monitor.
  4399.  *
  4400.  *        We need to intercept DOS packets twice -- once on the initial PutMsg
  4401.  *        to the target device, and once when the target device PutMsg's the
  4402.  *        reply. Thus, we maintain a list of outstanding packets so we can
  4403.  *        detect which is which.
  4404.  *
  4405.  *        To identify DOS packets, we use the following criterion:
  4406.  *
  4407.  *            - Task is a process
  4408.  *            - Message node's name points to valid longword-aligned memory
  4409.  *            - DOS packet (from name ptr) points back to message header
  4410.  *            - Either current task is on the list of devices we're monitoring
  4411.  *              or the destination message port's owner is on the list of
  4412.  *              devices we're monitoring.
  4413.  *        
  4414.  *        If any of these conditions fail, we ignore the message.
  4415.  *
  4416.  */        
  4417. ULONG ASM New_PutMsg(reg_a0 struct MsgPort *port, reg_a1 struct Message *msg,
  4418.                      reg_a6 void *libbase)
  4419. {
  4420.     MarkCallAddr;
  4421.  
  4422.     FP_PutMsg origfunc   = (FP_PutMsg)PatchList[GID_SENDREXX].origfunc;
  4423.     Task *thistask         = SysBase->ThisTask;
  4424.  
  4425. /*
  4426.  *        Save a little stack space and time by making these non-local
  4427.  */
  4428. #define rmsg            ((struct RexxMsg *)msg)
  4429. #define rexxclosevect     (ULONG *)(((ULONG)RexxSysBase) - 10)
  4430. #define thisclosevect    (ULONG *)(((ULONG)rmsg->rm_LibBase) - 10)
  4431.  
  4432.     struct DosPacket *dp;
  4433.     Task  *desttask;
  4434.     Event *ev;
  4435.     LONG  seqnum;
  4436.     ULONG taskhandle;
  4437.  
  4438.     /*
  4439.      *        Before doing anything else, check if we're being called from
  4440.      *        within an interrupt. If so, get the heck out of here since
  4441.      *        trying to obtain a semaphore from within an interrupt is
  4442.      *        definitely a bad idea!
  4443.      *
  4444.      *        To quickly check if we're within an interrupt, we simply see
  4445.      *        if our stack pointer is within the range of the supervisor stack.
  4446.      *        If it is, then we're probably in an interrupt, or at least in
  4447.      *        Supervisor mode, and either way we don't want to continue.
  4448.      *
  4449.      *        We also ignore ramlib, for the usual reasons, and our own
  4450.      *        background task (since it is used to expand locks from within
  4451.      *        of this patch and an infinite loop would be easy!)
  4452.      */
  4453.     if (thistask == RamLibTask || thistask == (Task *)BackgroundProc ||
  4454.         (getreg(REG_A7) >= (ULONG)SysBase->SysStkLower &&
  4455.          getreg(REG_A7) <= (ULONG)SysBase->SysStkUpper))
  4456.     {
  4457.         JumpOrigFunc(0);
  4458.     }
  4459.  
  4460.     /*
  4461.      *        See if we have a valid ARexx message. See the function
  4462.      *        header for an explanation of all the checks
  4463.      */
  4464.     if (CurSettings.Func.Opts[GID_SENDREXX]            &&
  4465.         port->mp_Node.ln_Name                        &&
  4466.         *port->mp_Node.ln_Name                        &&
  4467.         rmsg->rm_CommAddr                            &&
  4468.         TypeOfMem(rmsg->rm_CommAddr)                &&
  4469.         (rmsg->rm_Action & RXCODEMASK) == RXCOMM    &&
  4470.         RexxSysBase                                    &&
  4471.         TypeOfMem(thisclosevect)                    &&
  4472.         ((ULONG)thisclosevect & 1) == 0             &&
  4473.         *thisclosevect == *rexxclosevect)
  4474.     {
  4475.         /*
  4476.          *        Okay, we know we've got a valid Rexx message so now enter
  4477.          *        it into the event buffer. First, however, check that we
  4478.          *        are within our stack bounds (since the normal check done
  4479.          *        in the assembly patch code is bypassed for this function
  4480.          *        to accomodate RAM returning DOS packets).
  4481.          */
  4482.         if (getreg(REG_A7) >= (ULONG)thistask->tc_SPLower &&
  4483.              getreg(REG_A7) <= (ULONG)thistask->tc_SPUpper &&
  4484.              getreg(REG_A7) <=
  4485.                          ((ULONG)thistask->tc_SPLower+CurSettings.StackLimit))
  4486.         {
  4487.             JumpOrigFunc(0);
  4488.         }
  4489.         ev = CreateEvent(CallAddr, &seqnum, MSG_ACT_SENDREXX, rmsg->rm_Args[0],
  4490.                          port->mp_Node.ln_Name, NO_EXPAND);
  4491.         if (!ev)
  4492.             return origfunc(port, msg, libbase);
  4493.  
  4494.         ev->status = ES_UPDATING;
  4495.         CheckForPause(ev, seqnum);
  4496.         ObtainSemaphore(&BufSem);
  4497.         if (seqnum >= RealFirstSeq) {
  4498.             ev->status = ES_READY;
  4499.             Signal(SnoopTask, NewEventMask);
  4500.         }
  4501.         ReleaseSemaphore(&BufSem);
  4502.         return origfunc(port, msg, libbase);
  4503.     }
  4504.  
  4505.     /*
  4506.      *        Not an ARexx message, now check if it's a DOS packet.
  4507.      *        First though, we must check to see if we're already in
  4508.      *        the middle of a SnoopDos patch -- if so, we could end up
  4509.      *        recursing forever unless we don't try and monitor this one.
  4510.      *
  4511.      *        See above for the criteria we use to spot packet messages.
  4512.      */
  4513.     taskhandle = CheckTaskRecursion();
  4514.     if (!taskhandle)
  4515.         return origfunc(port, msg, libbase);
  4516.  
  4517.     dp         = (struct DosPacket *)(msg->mn_Node.ln_Name);
  4518.     desttask = port->mp_SigTask;
  4519.  
  4520.     if ((ShowAllPackets | MonPackets)                            &&
  4521.         thistask->tc_Node.ln_Type == NT_PROCESS                    &&
  4522.         ((ULONG)dp & 1) == 0                                    &&
  4523.         TypeOfMem(dp)                                            &&
  4524.         dp->dp_Link == msg                                        &&
  4525.         ((ULONG)(dp->dp_Port) & 3) == 0                            &&
  4526.         TypeOfMem(dp->dp_Port))
  4527.     {
  4528.         /*
  4529.          *        Looks like we're going to monitor the packet.
  4530.          *
  4531.          *        First, check if the packet is already on our list of
  4532.          *        outstanding packets. If so, we can handle it immediately.
  4533.          */
  4534.         struct PacketRef *pt;
  4535.         WaitPacket *wp;
  4536.         Task **ptask;
  4537.         char *p = NULL;
  4538.  
  4539.         /*
  4540.          *        Now some slightly tricky stuff. We can have up to TWO
  4541.          *        entries on the waiting packet list which match the current
  4542.          *        packet (one for the Packet Debugger and one for the
  4543.          *        Monitor Packets option) and we need to acknowledge them
  4544.          *        both. However, we also need to ensure we never try and
  4545.          *        lock both the PacketSem and BufferSem at the same time
  4546.          *        (since otherwise we could deadlock).
  4547.          *
  4548.          *        This effectively means we have to restart our list scan
  4549.          *        after each packet has been acknowledged, until we have
  4550.          *        no more left -- a small speed hit, but actually not too
  4551.          *        bad since most times, we will be the only packet on the
  4552.          *        wait list anyway. (We use 'p' as a flag to indicate
  4553.          *        whether or not we found a packet, so we know to return
  4554.          *        at the end of it all.)
  4555.          */
  4556.         ObtainSemaphore(&PacketSem);
  4557.         wp = HeadNode(&PacketWaitList);
  4558.         while (NextNode(wp)) {
  4559.             if (wp->dp         == dp                    &&
  4560.                 wp->sendtask != thistask            && 
  4561.                 wp->destport != port                &&
  4562.                 wp->arg1     == dp->dp_Arg1            &&
  4563.                 wp->arg2     == dp->dp_Arg2            &&
  4564.                 wp->arg3     == dp->dp_Arg3)
  4565.             {
  4566.                 /*
  4567.                  *        Found the packet on our list, so go ahead and fill in
  4568.                  *        the result code for the associated event (assuming it
  4569.                  *        hasn't scrolled off the buffer, of course!)
  4570.                  */
  4571.                 Event *ev       = wp->event;
  4572.                 LONG   seqnum = wp->eventnum;
  4573.                 char  *fmsg   = wp->resmsg;
  4574.                 char   flags  = wp->flags;
  4575.  
  4576.                 Remove((Node *)wp);
  4577.                 AddHead(&PacketFreeList, (Node *)wp);
  4578.                 ReleaseSemaphore(&PacketSem);
  4579.                 ObtainSemaphore(&BufSem);
  4580.                 if (seqnum >= RealFirstSeq) {
  4581.                     /*
  4582.                      *        Okay, now fill in the result string. The space for
  4583.                      *        this was allocated when the event was defined.
  4584.                      *
  4585.                      *        For events that succeed, we can print "Okay",
  4586.                      *        "Okay <result>", and "Okay <result1> <result2>".
  4587.                      *
  4588.                      *        For events which fail, we can print
  4589.                      *        "Fail <result2>" or "Fail <result1> <result2>"
  4590.                      */
  4591.                     UWORD fail;
  4592.                     ULONG param;
  4593.                     char  *q, *r;
  4594.  
  4595. #define                      evresmsg    MSG(fail ? MSG_RES_FAIL : MSG_RES_OKAY)
  4596.  
  4597.                      fail = ((flags & PKF_BOOL) && dp->dp_Res1 == 0)     ||
  4598.                            ((flags & PKF_NEG)  && dp->dp_Res1 == (ULONG)-1);
  4599.  
  4600. #if NOT_WORRIED_ABOUT_STACK_SIZE
  4601.                     /*
  4602.                      *        Unfortunately, we can't use this nice simple
  4603.                      *        code because calling mysprintf() uses too much
  4604.                      *        stack space if we're being called from certain
  4605.                      *        handlers with minimal stack space (e.g. RAM).
  4606.                      *        Instead, we must do the same effect ourselves.
  4607.                      *        This code is left here so you can see what the
  4608.                      *        original intention was.
  4609.                      */
  4610.                     if ((flags & PK_MASK) == PK_2OK) {
  4611.                         mysprintf(ev->result, fmsg, evresmsg,
  4612.                                   dp->dp_Res1, dp->dp_Res2);
  4613.                     } else {
  4614.                         mysprintf(ev->result, fmsg, evresmsg, 
  4615.                                   (fail ? dp->dp_Res2 : dp->dp_Res1));
  4616.                     }
  4617.                     p = (char *)-1;    /* Non-zero means we did something */
  4618.  
  4619. #else /* Extremely worried about stack size */
  4620.  
  4621.                     /*
  4622.                      *        Here we roll our own sprintf() (inline!) to keep
  4623.                      *        stack usage to an absolute minimum. It's a rather
  4624.                      *        small subset and somewhat hacked, you'll notice.
  4625.                      *
  4626.                      *        We make P non-null to indicate we did some work
  4627.                      */
  4628.                     p = fmsg;
  4629.                     q = ev->result;
  4630.  
  4631.                     /*
  4632.                      *        First, skip over string formatter at start which
  4633.                      *        always holds either 'Ok' or 'Fail'
  4634.                      */
  4635.                     strcpy(q, evresmsg);
  4636.                     q += strlen(q);
  4637.                     while (q < (ev->result + 4))
  4638.                         *q++ = ' ';
  4639.                     while (*p && *p != 's')
  4640.                         p++;
  4641.                     if (*p)
  4642.                         p++;
  4643.  
  4644.                     /*
  4645.                      *        Now parse remainder of string. The first parameter
  4646.                      *        encountered gets replaced with either dp_Res1 or
  4647.                      *        dp_Res2, depending on circumstances. The second
  4648.                      *        parameter always gets replaced with dp_Res2.
  4649.                      */
  4650.                     if ((flags & PK_MASK) == PK_2OK || !fail)
  4651.                         param = dp->dp_Res1;
  4652.                     else
  4653.                         param = dp->dp_Res2;
  4654.  
  4655.                     while (*p) {
  4656.                         if (*p != '%') {
  4657.                             *q++ = *p++;
  4658.                             continue;
  4659.                         }
  4660.                         while (*p && *p != 'x' && *p != 'X')
  4661.                             p++;
  4662.                         p++;
  4663.                         if (param == 0) {
  4664.                             strcpy(q, "       0");
  4665.                         } else if (param == (ULONG)-1) {
  4666.                             strcpy(q, "      -1");
  4667.                         } else {
  4668.                             /*
  4669.                              *        Expand param to 8 hex digits, right-aligned
  4670.                              */
  4671.                             r = q + 7;
  4672.                             while (param > 0) {
  4673.                                 *r-- = "0123456789ABCDEF"[param & 15];
  4674.                                 param >>= 4;
  4675.                             }
  4676.                             while (r >= q)
  4677.                                 *r-- = ' ';
  4678.                         }
  4679.                         q += 8;
  4680.                         param = dp->dp_Res2; /* Second parm always dp_Res2 */
  4681.                     }
  4682.                     *q = '\0';
  4683. #endif
  4684.                     /*
  4685.                      *        Unlike most times, we don't signal SnoopDos that
  4686.                      *        an event has been updated (yet) since there may
  4687.                      *        be another event to update too, and it's best
  4688.                      *        to do a single signal at the end (fewer context
  4689.                      *        switches are required.) So, we just mark the
  4690.                      *        event as ready.
  4691.                      */
  4692.                     ev->status = ES_READY;
  4693.                 }
  4694.                 ReleaseSemaphore(&BufSem);
  4695.                 ObtainSemaphore(&PacketSem);
  4696.                 /*
  4697.                  *        Now restart the search from the beginning of the
  4698.                  *        list (since wp is no longer valid)
  4699.                  */
  4700.                 wp = HeadNode(&PacketWaitList);
  4701.             } else {
  4702.                 /*
  4703.                  *        Just advance to next element on list
  4704.                  */
  4705.                 wp = NextNode(wp);
  4706.             }
  4707.         }
  4708.         ReleaseSemaphore(&PacketSem);
  4709.         if (p) {
  4710.             /*
  4711.              *        Found a matching packet, so wake up the main task
  4712.              *        and finish up. (We only signal the main snoop task
  4713.              *        now, so that both events get printed simultaneously,
  4714.              *        rather than one after another, which would be a little
  4715.              *        messy). This also avoids us getting pre-empted inside
  4716.              *        our loop above if SnoopDos is running at a higher priority
  4717.              *        than this task, which is convenient (though not essential).
  4718.              */
  4719.             Signal(SnoopTask, NewEventMask);
  4720.             goto done_putmsg;
  4721.         }
  4722.  
  4723.         /*
  4724.          *        If we didn't find a match on our list of waiting packets,
  4725.          *        then check to see if we're ignoring ROM calls. If so,
  4726.          *        and if we were called from ROM, then immediately abort
  4727.          *        (this would be done later anyway, but by checking now,
  4728.          *        we save quite a bit of time.)
  4729.          *
  4730.          *        (We can't do this checking any earlier, or we'd miss
  4731.          *        returned packets from ROM filesystems like RAM: and FFS).
  4732.          *
  4733.          *        We also exit if we're under the stack limit. (We can't
  4734.          *        check this earlier because otherwise we'd miss the
  4735.          *        return packet from RAM which only has a tiny stack.)
  4736.          */
  4737.         if (((!MonROMCalls && CallAddr >= RomStart && CallAddr <= RomEnd)) ||
  4738.             (getreg(REG_A7) >= (ULONG)thistask->tc_SPLower &&
  4739.              getreg(REG_A7) <= (ULONG)thistask->tc_SPUpper &&
  4740.              getreg(REG_A7) <=
  4741.                          ((ULONG)thistask->tc_SPLower+CurSettings.StackLimit)))
  4742.         {
  4743.             goto done_putmsg;
  4744.         }
  4745.  
  4746.         /*
  4747.          *        Okay, didn't match the packet with a previous outgoing one.
  4748.          *        Now search the device list to see if we can match either
  4749.          *        the owner of the port (a new outgoing packet) or the task ID
  4750.          *        of the sender (a returning packet).
  4751.          *
  4752.          *        In the case of a returning packet, we just ignore it since
  4753.          *        we missed it on the way out.
  4754.          */
  4755.         for (ptask = DeviceTaskList; *ptask && *ptask != desttask; ptask++) {
  4756.             if (*ptask == thistask)
  4757.                 goto done_putmsg;    /* Sender was a DOS device so ignore */
  4758.         }
  4759.         if (!*ptask) {
  4760.             /*
  4761.              *        Couldn't find dest task on device list so it must
  4762.              *        be a message to somewhere else -- hence, ignore it.
  4763.              */
  4764.             goto done_putmsg;
  4765.         }
  4766.  
  4767.         /*
  4768.          *        Okay, got ourselves a bona fida packet. Next, look it up
  4769.          *        in our packet table to decide what to do with it.
  4770.          *        Unrecognised packets come out as LAST_PACK_MSG which is
  4771.          *        specifically initialised to handle them properly.
  4772.          */
  4773.         for (pt = &PacketTable[0]; pt->msgid != LAST_PACK_MSG; pt++)
  4774.             if (pt->packetid == dp->dp_Type)
  4775.                 break;
  4776.         
  4777.         if (pt->flags & PK_IGNORE)    /* Certain packet types are ignored */
  4778.             goto done_putmsg;
  4779.  
  4780.         /*
  4781.          *        Okay, we finally (finally!) get to create a new entry
  4782.          *        in our event buffer. Actually, we get to create two
  4783.          *        entries, since if we have both the Packet Debugger
  4784.          *        AND the Monitor Packets option turned on, we want to
  4785.          *        see the output from both of them (even if they're both
  4786.          *        the same event!).
  4787.          *
  4788.          *        Why? Well, if you're debugging a device driver and you
  4789.          *        have Open packets etc. coming back and forth, then 
  4790.          *        as well as the raw packet data, it's also kind of nice
  4791.          *        to see actual filenames etc. in english instead of hex.
  4792.          */
  4793.         if (MonPackets && (pt->flags & PK_COMMON))
  4794.             HandleSimplePacket(CallAddr, dp, port, pt);
  4795.  
  4796.         if (ShowAllPackets)
  4797.             HandleRawPacket(CallAddr, dp, port, pt);
  4798.     }
  4799.  
  4800. done_putmsg:
  4801.     EndTaskRecursion(taskhandle);
  4802.     return origfunc(port, msg, libbase);
  4803. }
  4804.